aboutsummaryrefslogtreecommitdiff
path: root/compiler-rt/lib/dfsan/dfsan_custom.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
commitb1c73532ee8997fe5dfbeb7d223027bdf99758a0 (patch)
tree7d6e51c294ab6719475d660217aa0c0ad0526292 /compiler-rt/lib/dfsan/dfsan_custom.cpp
parent7fa27ce4a07f19b07799a767fc29416f3b625afb (diff)
downloadsrc-b1c73532ee8997fe5dfbeb7d223027bdf99758a0.tar.gz
src-b1c73532ee8997fe5dfbeb7d223027bdf99758a0.zip
Diffstat (limited to 'compiler-rt/lib/dfsan/dfsan_custom.cpp')
-rw-r--r--compiler-rt/lib/dfsan/dfsan_custom.cpp352
1 files changed, 347 insertions, 5 deletions
diff --git a/compiler-rt/lib/dfsan/dfsan_custom.cpp b/compiler-rt/lib/dfsan/dfsan_custom.cpp
index f41dd50617fb..38371d353368 100644
--- a/compiler-rt/lib/dfsan/dfsan_custom.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_custom.cpp
@@ -2240,8 +2240,14 @@ typedef int dfsan_label_va;
// '%.3f').
struct Formatter {
Formatter(char *str_, const char *fmt_, size_t size_)
- : str(str_), str_off(0), size(size_), fmt_start(fmt_), fmt_cur(fmt_),
- width(-1) {}
+ : str(str_),
+ str_off(0),
+ size(size_),
+ fmt_start(fmt_),
+ fmt_cur(fmt_),
+ width(-1),
+ num_scanned(-1),
+ skip(false) {}
int format() {
char *tmp_fmt = build_format_string();
@@ -2266,12 +2272,50 @@ struct Formatter {
return retval;
}
- char *build_format_string() {
+ int scan() {
+ char *tmp_fmt = build_format_string(true);
+ int read_count = 0;
+ int retval = sscanf(str + str_off, tmp_fmt, &read_count);
+ if (retval > 0) {
+ if (-1 == num_scanned)
+ num_scanned = 0;
+ num_scanned += retval;
+ }
+ free(tmp_fmt);
+ return read_count;
+ }
+
+ template <typename T>
+ int scan(T arg) {
+ char *tmp_fmt = build_format_string(true);
+ int read_count = 0;
+ int retval = sscanf(str + str_off, tmp_fmt, arg, &read_count);
+ if (retval > 0) {
+ if (-1 == num_scanned)
+ num_scanned = 0;
+ num_scanned += retval;
+ }
+ free(tmp_fmt);
+ return read_count;
+ }
+
+ // with_n -> toggles adding %n on/off; off by default
+ char *build_format_string(bool with_n = false) {
size_t fmt_size = fmt_cur - fmt_start + 1;
- char *new_fmt = (char *)malloc(fmt_size + 1);
+ size_t add_size = 0;
+ if (with_n)
+ add_size = 2;
+ char *new_fmt = (char *)malloc(fmt_size + 1 + add_size);
assert(new_fmt);
internal_memcpy(new_fmt, fmt_start, fmt_size);
- new_fmt[fmt_size] = '\0';
+ if (!with_n) {
+ new_fmt[fmt_size] = '\0';
+ } else {
+ new_fmt[fmt_size] = '%';
+ new_fmt[fmt_size + 1] = 'n';
+ new_fmt[fmt_size + 2] = '\0';
+ }
+
return new_fmt;
}
@@ -2303,6 +2347,8 @@ struct Formatter {
const char *fmt_start;
const char *fmt_cur;
int width;
+ int num_scanned;
+ bool skip;
};
// Formats the input and propagates the input labels to the output. The output
@@ -2495,6 +2541,249 @@ static int format_buffer(char *str, size_t size, const char *fmt,
return formatter.str_off;
}
+// This function is an inverse of format_buffer: we take the input buffer,
+// scan it in search for format strings and store the results in the varargs.
+// The labels are propagated from the input buffer to the varargs.
+static int scan_buffer(char *str, size_t size, const char *fmt,
+ dfsan_label *va_labels, dfsan_label *ret_label,
+ dfsan_origin *str_origin, dfsan_origin *ret_origin,
+ va_list ap) {
+ Formatter formatter(str, fmt, size);
+ while (*formatter.fmt_cur) {
+ formatter.fmt_start = formatter.fmt_cur;
+ formatter.width = -1;
+ formatter.skip = false;
+ int read_count = 0;
+ void *dst_ptr = 0;
+ size_t write_size = 0;
+ if (*formatter.fmt_cur != '%') {
+ // Ordinary character. Consume all the characters until a '%' or the end
+ // of the string.
+ for (; *(formatter.fmt_cur + 1) && *(formatter.fmt_cur + 1) != '%';
+ ++formatter.fmt_cur) {
+ }
+ read_count = formatter.scan();
+ dfsan_set_label(0, formatter.str_cur(),
+ formatter.num_written_bytes(read_count));
+ } else {
+ // Conversion directive. Consume all the characters until a conversion
+ // specifier or the end of the string.
+ bool end_fmt = false;
+ for (; *formatter.fmt_cur && !end_fmt;) {
+ switch (*++formatter.fmt_cur) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (formatter.skip) {
+ read_count = formatter.scan();
+ } else {
+ switch (*(formatter.fmt_cur - 1)) {
+ case 'h':
+ // Also covers the 'hh' case (since the size of the arg is still
+ // an int).
+ dst_ptr = va_arg(ap, int *);
+ read_count = formatter.scan((int *)dst_ptr);
+ write_size = sizeof(int);
+ break;
+ case 'l':
+ if (formatter.fmt_cur - formatter.fmt_start >= 2 &&
+ *(formatter.fmt_cur - 2) == 'l') {
+ dst_ptr = va_arg(ap, long long int *);
+ read_count = formatter.scan((long long int *)dst_ptr);
+ write_size = sizeof(long long int);
+ } else {
+ dst_ptr = va_arg(ap, long int *);
+ read_count = formatter.scan((long int *)dst_ptr);
+ write_size = sizeof(long int);
+ }
+ break;
+ case 'q':
+ dst_ptr = va_arg(ap, long long int *);
+ read_count = formatter.scan((long long int *)dst_ptr);
+ write_size = sizeof(long long int);
+ break;
+ case 'j':
+ dst_ptr = va_arg(ap, intmax_t *);
+ read_count = formatter.scan((intmax_t *)dst_ptr);
+ write_size = sizeof(intmax_t);
+ break;
+ case 'z':
+ case 't':
+ dst_ptr = va_arg(ap, size_t *);
+ read_count = formatter.scan((size_t *)dst_ptr);
+ write_size = sizeof(size_t);
+ break;
+ default:
+ dst_ptr = va_arg(ap, int *);
+ read_count = formatter.scan((int *)dst_ptr);
+ write_size = sizeof(int);
+ }
+ // get the label associated with the string at the corresponding
+ // place
+ dfsan_label l = dfsan_read_label(
+ formatter.str_cur(), formatter.num_written_bytes(read_count));
+ dfsan_set_label(l, dst_ptr, write_size);
+ if (str_origin != nullptr) {
+ dfsan_set_label(l, dst_ptr, write_size);
+ size_t scan_count = formatter.num_written_bytes(read_count);
+ size_t size = scan_count > write_size ? write_size : scan_count;
+ dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(), size);
+ }
+ }
+ end_fmt = true;
+
+ break;
+
+ case 'a':
+ case 'A':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ if (formatter.skip) {
+ read_count = formatter.scan();
+ } else {
+ if (*(formatter.fmt_cur - 1) == 'L') {
+ dst_ptr = va_arg(ap, long double *);
+ read_count = formatter.scan((long double *)dst_ptr);
+ write_size = sizeof(long double);
+ } else if (*(formatter.fmt_cur - 1) == 'l') {
+ dst_ptr = va_arg(ap, double *);
+ read_count = formatter.scan((double *)dst_ptr);
+ write_size = sizeof(double);
+ } else {
+ dst_ptr = va_arg(ap, float *);
+ read_count = formatter.scan((float *)dst_ptr);
+ write_size = sizeof(float);
+ }
+ dfsan_label l = dfsan_read_label(
+ formatter.str_cur(), formatter.num_written_bytes(read_count));
+ dfsan_set_label(l, dst_ptr, write_size);
+ if (str_origin != nullptr) {
+ dfsan_set_label(l, dst_ptr, write_size);
+ size_t scan_count = formatter.num_written_bytes(read_count);
+ size_t size = scan_count > write_size ? write_size : scan_count;
+ dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(), size);
+ }
+ }
+ end_fmt = true;
+ break;
+
+ case 'c':
+ if (formatter.skip) {
+ read_count = formatter.scan();
+ } else {
+ dst_ptr = va_arg(ap, char *);
+ read_count = formatter.scan((char *)dst_ptr);
+ write_size = sizeof(char);
+ dfsan_label l = dfsan_read_label(
+ formatter.str_cur(), formatter.num_written_bytes(read_count));
+ dfsan_set_label(l, dst_ptr, write_size);
+ if (str_origin != nullptr) {
+ size_t scan_count = formatter.num_written_bytes(read_count);
+ size_t size = scan_count > write_size ? write_size : scan_count;
+ dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(), size);
+ }
+ }
+ end_fmt = true;
+ break;
+
+ case 's': {
+ if (formatter.skip) {
+ read_count = formatter.scan();
+ } else {
+ dst_ptr = va_arg(ap, char *);
+ read_count = formatter.scan((char *)dst_ptr);
+ if (1 == read_count) {
+ // special case: we have parsed a single string and we need to
+ // update read_count with the string size
+ read_count = strlen((char *)dst_ptr);
+ }
+ if (str_origin)
+ dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(),
+ formatter.num_written_bytes(read_count));
+ va_labels++;
+ dfsan_mem_shadow_transfer(dst_ptr, formatter.str_cur(),
+ formatter.num_written_bytes(read_count));
+ }
+ end_fmt = true;
+ break;
+ }
+
+ case 'p':
+ if (formatter.skip) {
+ read_count = formatter.scan();
+ } else {
+ dst_ptr = va_arg(ap, void *);
+ read_count =
+ formatter.scan((int *)dst_ptr); // note: changing void* to int*
+ // since we need to call sizeof
+ write_size = sizeof(int);
+
+ dfsan_label l = dfsan_read_label(
+ formatter.str_cur(), formatter.num_written_bytes(read_count));
+ dfsan_set_label(l, dst_ptr, write_size);
+ if (str_origin != nullptr) {
+ dfsan_set_label(l, dst_ptr, write_size);
+ size_t scan_count = formatter.num_written_bytes(read_count);
+ size_t size = scan_count > write_size ? write_size : scan_count;
+ dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(), size);
+ }
+ }
+ end_fmt = true;
+ break;
+
+ case 'n': {
+ if (!formatter.skip) {
+ int *ptr = va_arg(ap, int *);
+ *ptr = (int)formatter.str_off;
+ *va_labels++ = 0;
+ dfsan_set_label(0, ptr, sizeof(*ptr));
+ if (str_origin != nullptr)
+ *str_origin++ = 0;
+ }
+ end_fmt = true;
+ break;
+ }
+
+ case '%':
+ read_count = formatter.scan();
+ end_fmt = true;
+ break;
+
+ case '*':
+ formatter.skip = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (read_count < 0) {
+ // There was an error.
+ return read_count;
+ }
+
+ formatter.fmt_cur++;
+ formatter.str_off += read_count;
+ }
+
+ (void)va_labels; // Silence unused-but-set-parameter warning
+ *ret_label = 0;
+ if (ret_origin)
+ *ret_origin = 0;
+
+ // Number of items scanned in total.
+ return formatter.num_scanned;
+}
+
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label,
@@ -2502,6 +2791,7 @@ int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label,
dfsan_label *ret_label, ...) {
va_list ap;
va_start(ap, ret_label);
+
int ret = format_buffer(str, ~0ul, format, va_labels, ret_label, nullptr,
nullptr, ap);
va_end(ap);
@@ -2550,6 +2840,58 @@ int __dfso_snprintf(char *str, size_t size, const char *format,
return ret;
}
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfsw_sscanf(char *str, const char *format, dfsan_label str_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, ...) {
+ va_list ap;
+ va_start(ap, ret_label);
+ int ret = scan_buffer(str, ~0ul, format, va_labels, ret_label, nullptr,
+ nullptr, ap);
+ va_end(ap);
+ return ret;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfso_sscanf(char *str, const char *format, dfsan_label str_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, dfsan_origin str_origin,
+ dfsan_origin format_origin, dfsan_origin *va_origins,
+ dfsan_origin *ret_origin, ...) {
+ va_list ap;
+ va_start(ap, ret_origin);
+ int ret = scan_buffer(str, ~0ul, format, va_labels, ret_label, &str_origin,
+ ret_origin, ap);
+ va_end(ap);
+ return ret;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfsw___isoc99_sscanf(char *str, const char *format, dfsan_label str_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, ...) {
+ va_list ap;
+ va_start(ap, ret_label);
+ int ret = scan_buffer(str, ~0ul, format, va_labels, ret_label, nullptr,
+ nullptr, ap);
+ va_end(ap);
+ return ret;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfso___isoc99_sscanf(char *str, const char *format, dfsan_label str_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, dfsan_origin str_origin,
+ dfsan_origin format_origin, dfsan_origin *va_origins,
+ dfsan_origin *ret_origin, ...) {
+ va_list ap;
+ va_start(ap, ret_origin);
+ int ret = scan_buffer(str, ~0ul, format, va_labels, ret_label, &str_origin,
+ ret_origin, ap);
+ va_end(ap);
+ return ret;
+}
+
static void BeforeFork() {
StackDepotLockAll();
GetChainedOriginDepot()->LockAll();