commit b84a5e8c2c5a857c0790a71df7824259a95131cf Author: William Cohen Date: Mon Dec 4 11:28:10 2023 -0500 PR31074: Ensure that the set_kernel_string* functions limit their writes Both the set_kernel_string and set_kernel_string_n function use the underlying _stp_store_deref_string_ function to write strings. There were two issues with the this function: 1) wrote MAXSTRINGLEN bytes even if string was shorter 2) null write at end could spill past end of buffer The first issue was addressed by stopping to write once a null character is encountered. The second issue is a side effect of C implicit promotion of character constants to ints and was addressed by explicitlying casting the character constants as a char. The pr31074.exp test was added to verify that the write length are limited to string length and the null write does not go beyond the end of the buffer. diff --git a/runtime/linux/loc2c-runtime.h b/runtime/linux/loc2c-runtime.h index 68fbe2ab6..663360293 100644 --- a/runtime/linux/loc2c-runtime.h +++ b/runtime/linux/loc2c-runtime.h @@ -1007,11 +1007,14 @@ static inline int _stp_store_deref_string_(char *src, void *addr, size_t len, { for (i = 0; i < len - 1; ++i) { + if (*src == '\0') + break; err = __stp_put_either(*src++, (u8 *)addr + i, seg); if (err) goto out; } - err = __stp_put_either('\0', (u8 *)addr + i, seg); + /* PR31074: cast (char) '\0' to make sure right size */ + err = __stp_put_either((char) '\0', (u8 *)addr + i, seg); } out: diff --git a/testsuite/systemtap.base/pr31074.exp b/testsuite/systemtap.base/pr31074.exp new file mode 100644 index 000000000..5b382b789 --- /dev/null +++ b/testsuite/systemtap.base/pr31074.exp @@ -0,0 +1,5 @@ +# Check that the set_kernel_* functions work correctly. + +set test "pr31074" + +stap_run $test no_load $all_pass_string -g $srcdir/$subdir/$test.stp diff --git a/testsuite/systemtap.base/pr31074.stp b/testsuite/systemtap.base/pr31074.stp new file mode 100644 index 000000000..930c276b5 --- /dev/null +++ b/testsuite/systemtap.base/pr31074.stp @@ -0,0 +1,88 @@ +/* + * pr31074.stp + * + * Check that the set_kernel_string function work correctly. + */ + +probe begin { println("systemtap starting probe") } +probe end { println("systemtap ending probe") } + +global errors = 0 + +function assert_string(test, expected, value) +{ + if (value == expected) + return 1 + printf("systemtap test failure - %s: expected \"%s\", got \"%s\"\n", + test, expected, value) + errors++ + return 0 +} + +function assert_not_reached(test) +{ + printf("systemtap test failure - %s: missing exception\n", test) + errors++ +} + +function assert_buffer_untouched(test, addr) +{ + if (!buffer_42(addr)) { + printf("systemtap test failure - %s: buffer overwritten\n", test) + errors++ + } +} + + +probe end(1) +{ + test = "set_kernel_string" + addr3 = get_buffer3() + addr2 = get_buffer2() + if (assert_string(test, "", kernel_string(addr2))) { + set_kernel_string(addr2, "bar") + assert_string(test, "bar", kernel_string(addr2)) + } + addr1 = get_buffer1() + if (assert_string(test, "", kernel_string(addr1))) { + set_kernel_string(addr1, "foo") + assert_string(test, "foo", kernel_string(addr1)) + } + /* now check to make sure that "bar" has not been overwritten */ + assert_string("no null overrun", "bar", kernel_string(addr2)) + assert_buffer_untouched("no overrun", addr3) + if (!errors) + println("systemtap test success") +} + +%{ + static char buffer_x[4+4+MAXSTRINGLEN]; +%} + +function get_buffer1:long () %{ + static char *buffer1 = &(buffer_x[0]); + memset(buffer1, 0, 4); + STAP_RETVALUE = (long)buffer1; +%} + +function get_buffer2:long () %{ + static char *buffer2 = &(buffer_x[4]); + memset(buffer2, 0, 4); + STAP_RETVALUE = (long)buffer2; +%} + +function get_buffer3:long () %{ + static char *buffer3 = &(buffer_x[8]); + memset(buffer3, 42, MAXSTRINGLEN); + STAP_RETVALUE = (long)buffer3; +%} + +function buffer_42:long (addr:long) %{ + int i; + char *buffer3 = (char *)STAP_ARG_addr; + STAP_RETVALUE = 1; + for(i=0; i< MAXSTRINGLEN; ++i){ + if (buffer3[i] != 42) + STAP_RETVALUE = 0; + } +%}