diff mbox series

[ovs-dev,v2,4/8] tests: Add ASAN use-after-free validation with RCU

Message ID 8f94d7e841a268b797b861a3f75cbdfe3fa1d791.1621517561.git.grive@u256.net
State New
Headers show
Series RCU: Add blocking mode for debugging | expand

Commit Message

Gaetan Rivet May 20, 2021, 1:35 p.m. UTC
When using the RCU mechanism and deferring memory reclamation, potential
use-after-free due to incorrect use of RCU can be hidden.

Add a test triggering a UAF event. When the test suite is built with
AddressSanitizer support, verify that the event triggers and the tool is
usable with RCU.

Signed-off-by: Gaetan Rivet <grive@u256.net>
---
 tests/automake.mk    |  1 +
 tests/library.at     | 33 +++++++++++++++
 tests/test-rcu-uaf.c | 98 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+)
 create mode 100644 tests/test-rcu-uaf.c
diff mbox series

Patch

diff --git a/tests/automake.mk b/tests/automake.mk
index a32abd41c..4420a3f7f 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -472,6 +472,7 @@  tests_ovstest_SOURCES = \
 	tests/test-packets.c \
 	tests/test-random.c \
 	tests/test-rcu.c \
+	tests/test-rcu-uaf.c \
 	tests/test-reconnect.c \
 	tests/test-rstp.c \
 	tests/test-sflow.c \
diff --git a/tests/library.at b/tests/library.at
index 6e8a154e5..4a549f77e 100644
--- a/tests/library.at
+++ b/tests/library.at
@@ -261,6 +261,39 @@  AT_KEYWORDS([rcu])
 AT_CHECK([ovstest test-rcu], [0], [])
 AT_CLEANUP
 
+AT_SETUP([rcu quiesce use-after-free detection])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+AT_SKIP_IF([test "$ASAN_ENABLED" = "no"])
+# SIGABRT + 128
+exit_status=134
+AT_KEYWORDS([rcu asan])
+AT_CHECK([ovstest test-rcu-uaf quiesce], [$exit_status], [ignore], [ignore])
+# ASAN report is expected on success.
+rm asan.*
+AT_CLEANUP
+
+AT_SETUP([rcu try-quiesce use-after-free detection])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+AT_SKIP_IF([test "$ASAN_ENABLED" = "no"])
+# SIGABRT + 128
+exit_status=134
+AT_KEYWORDS([rcu asan])
+AT_CHECK([ovstest test-rcu-uaf try-quiesce], [$exit_status], [ignore], [ignore])
+# ASAN report is expected on success.
+rm asan.*
+AT_CLEANUP
+
+AT_SETUP([rcu quiesce-start-end use-after-free detection])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+AT_SKIP_IF([test "$ASAN_ENABLED" = "no"])
+AT_KEYWORDS([rcu asan])
+# SIGABRT + 128
+exit_status=134
+AT_CHECK([ovstest test-rcu-uaf quiesce-start-end], [$exit_status], [ignore], [ignore])
+# ASAN report is expected on success.
+rm asan.*
+AT_CLEANUP
+
 AT_SETUP([stopwatch module])
 AT_CHECK([ovstest test-stopwatch], [0], [......
 ], [ignore])
diff --git a/tests/test-rcu-uaf.c b/tests/test-rcu-uaf.c
new file mode 100644
index 000000000..f97738795
--- /dev/null
+++ b/tests/test-rcu-uaf.c
@@ -0,0 +1,98 @@ 
+/*
+ * Copyright (c) 2021 NVIDIA Corporation.
+ *
+ * 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 <getopt.h>
+
+#include <config.h>
+
+#include "ovs-thread.h"
+#include "ovs-rcu.h"
+#include "ovstest.h"
+#include "util.h"
+
+enum ovsrcu_uaf_type {
+    OVSRCU_UAF_QUIESCE,
+    OVSRCU_UAF_TRY_QUIESCE,
+    OVSRCU_UAF_QUIESCE_START_END,
+};
+
+static void *
+rcu_uaf_main(void *aux)
+{
+    enum ovsrcu_uaf_type *type = aux;
+    char *xx = xmalloc(2);
+
+    xx[0] = 'a';
+    ovsrcu_postpone(free, xx);
+    switch (*type) {
+    case OVSRCU_UAF_QUIESCE:
+        ovsrcu_quiesce();
+        break;
+    case OVSRCU_UAF_TRY_QUIESCE:
+        while (ovsrcu_try_quiesce()) {
+            ;
+        }
+        break;
+    case OVSRCU_UAF_QUIESCE_START_END:
+        ovsrcu_quiesce_start();
+        ovsrcu_quiesce_end();
+        break;
+    default:
+        OVS_NOT_REACHED();
+    }
+    xx[1] = 'b';
+
+    return NULL;
+}
+
+static void
+usage(char *test_name)
+{
+    fprintf(stderr, "Usage: %s <quiesce|try-quiesce|quiesce-start-end>\n",
+            test_name);
+}
+
+static void
+test_rcu_uaf(int argc, char *argv[])
+{
+    char **args = argv + optind - 1;
+    enum ovsrcu_uaf_type type;
+    pthread_t quiescer;
+
+    if (argc - optind != 1) {
+        usage(args[0]);
+        return;
+    }
+
+    set_program_name(argv[0]);
+
+    if (!strcmp(args[1], "quiesce")) {
+        type = OVSRCU_UAF_QUIESCE;
+    } else if (!strcmp(args[1], "try-quiesce")) {
+        type = OVSRCU_UAF_TRY_QUIESCE;
+    } else if (!strcmp(args[1], "quiesce-start-end")) {
+        type = OVSRCU_UAF_QUIESCE_START_END;
+    } else {
+        usage(args[0]);
+        return;
+    }
+
+    /* Need to create a separate thread, to support try-quiesce. */
+    quiescer = ovs_thread_create("rcu-uaf", rcu_uaf_main, &type);
+    xpthread_join(quiescer, NULL);
+}
+
+OVSTEST_REGISTER("test-rcu-uaf", test_rcu_uaf);