aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/unittest/api_fuzz_test.c
diff options
context:
space:
mode:
authorDave Barach <dave@barachs.net>2020-04-29 17:04:10 -0400
committerFlorin Coras <florin.coras@gmail.com>2020-05-04 14:03:21 +0000
commit7784140f2bd2d5ae44f2be1507ac25f102006155 (patch)
tree4ac12e04f5177bffab6f3c05d6bf837567a65988 /src/plugins/unittest/api_fuzz_test.c
parentd88fc0fcedb402e3dc82cb44280d4567a6a13266 (diff)
misc: binary api fuzz test fixes
Add a hook to src/vlibapi/api_shared.c to fuzz (screw up) binary API messages, e.g. by xoring random data into them before processing. We specifically exempt client connection messages, and inband debug CLI messages. We step over msg_id, client index, client context, and sw_if_index. Otherwise, "make test" vectors fail too rapidly to learn anything. The goal is to reduce the number of crashes caused to zero. We're fairly close with this patch. Add vl_msg_api_max_length(void *mp), which returns the maximum plausible length for a binary API message. Use it to hardern vl_api_from_api_to_new_vec(...) which takes an additional argument - message pointer - so it can verify that astr->length is sane. If it's not sane, return a u8 *vector of the form "insane astr->length nnnn\0". Verify array lengths in vl_api_dhcp6_send_client_message_t_handler(...) and vl_api_dhcp6_pd_send_client_message_t_handler(...). Add a fairly effective binary API fuzz hook to the unittest plugin, and modify the "make test" framework.py to pass "api-fuzz { on|off }" to enable API fuzzing: "make API_FUZZ=on TEST=xxx test-debug" or similar Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I0157267652a163c01553d5267620f719cc6c3bde
Diffstat (limited to 'src/plugins/unittest/api_fuzz_test.c')
-rw-r--r--src/plugins/unittest/api_fuzz_test.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/plugins/unittest/api_fuzz_test.c b/src/plugins/unittest/api_fuzz_test.c
new file mode 100644
index 00000000000..113835300bb
--- /dev/null
+++ b/src/plugins/unittest/api_fuzz_test.c
@@ -0,0 +1,187 @@
+/*
+ *------------------------------------------------------------------
+ * api_fuzz_test.c - Binary API fuzz hook
+ *
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+#include <vppinfra/format.h>
+#include <vppinfra/byte_order.h>
+#include <vppinfra/error.h>
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vlibapi/api.h>
+
+static u32 fuzz_seed = 0xdeaddabe;
+static u16 fuzz_first;
+static u16 fuzz_cli_first, fuzz_cli_last;
+
+extern void (*vl_msg_api_fuzz_hook) (u16, void *);
+
+static void
+fuzz_hook (u16 id, void *the_msg)
+{
+ /*
+ * Fuzz (aka screw up) this message? Leave connection establishment
+ * messages alone as well as CLI messages.
+ */
+ if ((id > fuzz_first) && !(id >= fuzz_cli_first && id < fuzz_cli_last))
+ {
+ msgbuf_t *mb;
+ u8 *limit, *start;
+
+ mb = (msgbuf_t *) (((u8 *) the_msg) - offsetof (msgbuf_t, data));
+
+ limit = (u8 *) (mb->data + ntohl (mb->data_len));
+
+ /*
+ * Leave the first 14 octets alone, aka msg_id, client_index,
+ * context, sw_if_index
+ */
+
+ start = ((u8 *) the_msg) + 14;
+
+ for (; start < limit; start++)
+ *start ^= (random_u32 (&fuzz_seed) & 0xFF);
+ }
+}
+
+static void
+default_fuzz_config (void)
+{
+ fuzz_first = vl_msg_api_get_msg_index
+ ((u8 *) "memclnt_keepalive_reply_e8d4e804");
+ fuzz_cli_first = vl_msg_api_get_msg_index ((u8 *) "cli_23bfbfff");
+ fuzz_cli_last = vl_msg_api_get_msg_index
+ ((u8 *) "cli_inband_reply_05879051");
+}
+
+static clib_error_t *
+test_api_fuzz_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 tmp;
+
+ default_fuzz_config ();
+
+ if (fuzz_first == 0xFFFF)
+ {
+ vlib_cli_output (vm, "Couldn't find 'memclnt_keepalive_reply' ID");
+ vlib_cli_output
+ (vm, "Manual setting required, use 'show api message table'");
+ }
+
+ if (fuzz_cli_first == 0xFFFF)
+ {
+ vlib_cli_output (vm, "Couldn't find 'cli' ID");
+ vlib_cli_output
+ (vm, "Manual setting required, use 'show api message table'");
+ }
+
+ if (fuzz_cli_last == 0xFFFF)
+ {
+ vlib_cli_output (vm, "Couldn't find 'cli_inband_reply' ID");
+ vlib_cli_output
+ (vm, "Manual setting required, use 'show api message table'");
+ }
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "seed %d", &fuzz_seed))
+ ;
+ else if (unformat (input, "disable") | unformat (input, "off"))
+ fuzz_first = ~0;
+ else if (unformat (input, "fuzz-first %d", &tmp))
+ fuzz_first = (u16) tmp;
+ else if (unformat (input, "fuzz-cli-first %d", &tmp))
+ fuzz_cli_first = (u16) tmp;
+ else if (unformat (input, "fuzz-cli-last %d", &tmp))
+ fuzz_cli_last = (u16) tmp;
+ else
+ break;
+ }
+
+ if (fuzz_first == 0xFFFF)
+ {
+ vl_msg_api_fuzz_hook = 0;
+ return clib_error_return (0, "fuzz_first is ~0, fuzzing disabled");
+ }
+ vl_msg_api_fuzz_hook = fuzz_hook;
+
+ vlib_cli_output (vm, "Fuzzing enabled: first %d, skip cli range %d - %d",
+ (u32) fuzz_first, (u32) fuzz_cli_first,
+ (u32) fuzz_cli_last);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (test_api_fuzz, static) = {
+ .path = "test api fuzz",
+ .short_help = "test api fuzz [disable][seed nnn]\n"
+ " [fuzz-first nn][fuzz-cli-first nn][fuzz-cli-last nn]",
+ .function = test_api_fuzz_command_fn,
+ };
+/* *INDENT-ON* */
+
+static u8 main_loop_enter_enable_api_fuzz;
+
+static clib_error_t *
+api_fuzz_config (vlib_main_t * vm, unformat_input_t * input)
+{
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "off")
+ || unformat (input, "disable") || unformat (input, "no"))
+ ; /* ok, no action */
+ else if (unformat (input, "on")
+ || unformat (input, "enable") || unformat (input, "yes"))
+ main_loop_enter_enable_api_fuzz = 1;
+ else
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
+ }
+ return 0;
+}
+
+VLIB_CONFIG_FUNCTION (api_fuzz_config, "api-fuzz");
+
+static clib_error_t *
+api_fuzz_api_init (vlib_main_t * vm)
+{
+ /* Are we supposed to fuzz API messages? */
+ if (main_loop_enter_enable_api_fuzz == 0)
+ return 0;
+
+ default_fuzz_config ();
+
+ if (fuzz_first == 0xFFFF)
+ {
+ return clib_error_return
+ (0, "Couldn't find 'memclnt_keepalive_reply' ID");
+ }
+ /* Turn on fuzzing */
+ vl_msg_api_fuzz_hook = fuzz_hook;
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (api_fuzz_api_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */