Index: compiler-rt/lib/dfsan/dfsan_custom.cpp =================================================================== --- compiler-rt/lib/dfsan/dfsan_custom.cpp +++ compiler-rt/lib/dfsan/dfsan_custom.cpp @@ -204,6 +204,59 @@ return const_cast(ret); } +SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strsep(char **s, const char *delim, + dfsan_label s_label, + dfsan_label delim_label, + dfsan_label *ret_label) { + char *base = *s; + char *res = strsep(s, delim); + s_label = dfsan_read_label(base, strlen(base)); + if (res && (res != base)) { + char *token_start = res; + int token_length = strlen(res); + // the delimiter byte has been set to NULL + dfsan_set_label(0, token_start + token_length, 1); + } + + if (flags().strict_data_dependencies) { + *ret_label = res ? s_label : 0; + } else { + size_t s_bytes_read = (res ? strlen(res) : strlen(base)) + 1; + *ret_label = + dfsan_union(dfsan_read_label(base, s_bytes_read), + dfsan_union(dfsan_read_label(delim, strlen(delim) + 1), + dfsan_union(s_label, delim_label))); + } + + return res; +} + +SANITIZER_INTERFACE_ATTRIBUTE char *__dfso_strsep( + char **s, const char *delim, dfsan_label s_label, dfsan_label delim_label, + dfsan_label *ret_label, dfsan_origin s_origin, dfsan_origin delim_origin, + dfsan_origin *ret_origin) { + char *base = *s; + s_origin = dfsan_read_origin_of_first_taint(base, strlen(base) + 1); + char *res = __dfsw_strsep(s, delim, s_label, delim_label, ret_label); + if (flags().strict_data_dependencies) { + if (res) + *ret_origin = s_origin; + } else { + if (*ret_label) { + size_t s_bytes_read = (res ? strlen(res) : strlen(base)) + 1; + dfsan_origin o = dfsan_read_origin_of_first_taint(base, s_bytes_read); + if (o) { + *ret_origin = o; + } else { + o = dfsan_read_origin_of_first_taint(delim, strlen(delim) + 1); + *ret_origin = o ? o : (s_label ? s_origin : delim_origin); + } + } + } + + return res; +} + static int dfsan_memcmp_bcmp(const void *s1, const void *s2, size_t n, size_t *bytes_read) { const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; @@ -484,6 +537,30 @@ return ret; } +SANITIZER_INTERFACE_ATTRIBUTE size_t __dfsw_strnlen(const char *s, + size_t maxlen, + dfsan_label s_label, + dfsan_label maxlen_label, + dfsan_label *ret_label) { + size_t ret = strnlen(s, maxlen); + if (flags().strict_data_dependencies) { + *ret_label = 0; + } else { + *ret_label = dfsan_read_label(s, ret + 1); + } + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE size_t __dfso_strnlen( + const char *s, size_t maxlen, dfsan_label s_label, dfsan_label maxlen_label, + dfsan_label *ret_label, dfsan_origin s_origin, dfsan_origin maxlen_origin, + dfsan_origin *ret_origin) { + size_t ret = __dfsw_strnlen(s, maxlen, s_label, maxlen_label, ret_label); + if (!flags().strict_data_dependencies) + *ret_origin = dfsan_read_origin_of_first_taint(s, ret + 1); + return ret; +} + static void *dfsan_memmove(void *dest, const void *src, size_t n) { dfsan_label *sdest = shadow_for(dest); const dfsan_label *ssrc = shadow_for(src); @@ -601,6 +678,37 @@ return ret; } +SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strncat( + char *dest, const char *src, size_t num, dfsan_label dest_label, + dfsan_label src_label, dfsan_label num_label, dfsan_label *ret_label) { + size_t src_len = strlen(src); + src_len = src_len < num ? src_len : num; + size_t dest_len = strlen(dest); + + char *ret = strncat(dest, src, num); + dfsan_mem_shadow_transfer(dest + dest_len, src, src_len); + *ret_label = dest_label; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE char *__dfso_strncat( + char *dest, const char *src, size_t num, dfsan_label dest_label, + dfsan_label src_label, dfsan_label num_label, dfsan_label *ret_label, + dfsan_origin dest_origin, dfsan_origin src_origin, dfsan_origin num_origin, + dfsan_origin *ret_origin) { + size_t src_len = strlen(src); + src_len = src_len < num ? src_len : num; + size_t dest_len = strlen(dest); + + char *ret = strncat(dest, src, num); + + dfsan_mem_origin_transfer(dest + dest_len, src, src_len); + dfsan_mem_shadow_transfer(dest + dest_len, src, src_len); + *ret_label = dest_label; + *ret_origin = dest_origin; + return ret; +} + SANITIZER_INTERFACE_ATTRIBUTE char * __dfsw_strdup(const char *s, dfsan_label s_label, dfsan_label *ret_label) { size_t len = strlen(s); @@ -2128,8 +2236,13 @@ // '%.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) {} int format() { char *tmp_fmt = build_format_string(); @@ -2154,12 +2267,50 @@ 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 + 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; } @@ -2191,6 +2342,7 @@ const char *fmt_start; const char *fmt_cur; int width; + int num_scanned; }; // Formats the input and propagates the input labels to the output. The output @@ -2383,6 +2535,214 @@ 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; + int retval = 0; + dfsan_label l = 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) { + } + retval = formatter.scan(); + dfsan_set_label(0, formatter.str_cur(), + formatter.num_written_bytes(retval)); + } 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': + 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 *); + retval = 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 *); + retval = formatter.scan((long long int *)dst_ptr); + write_size = sizeof(long long int); + } else { + dst_ptr = va_arg(ap, long int *); + retval = formatter.scan((long int *)dst_ptr); + write_size = sizeof(long int); + } + break; + case 'q': + dst_ptr = va_arg(ap, long long int *); + retval = formatter.scan((long long int *)dst_ptr); + write_size = sizeof(long long int); + break; + case 'j': + dst_ptr = va_arg(ap, intmax_t *); + retval = formatter.scan((intmax_t *)dst_ptr); + write_size = sizeof(intmax_t); + break; + case 'z': + case 't': + dst_ptr = va_arg(ap, size_t *); + retval = formatter.scan((size_t *)dst_ptr); + write_size = sizeof(size_t); + break; + default: + dst_ptr = va_arg(ap, int *); + retval = formatter.scan((int *)dst_ptr); + write_size = sizeof(int); + } + // get the label associated with the string at the corresponding + // place + l = dfsan_read_label(formatter.str_cur(), + formatter.num_written_bytes(retval)); + if (str_origin == nullptr) + dfsan_set_label(l, dst_ptr, write_size); + else + dfsan_set_label_origin(l, *str_origin, dst_ptr, write_size); + end_fmt = true; + + break; + + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (*(formatter.fmt_cur - 1) == 'L') { + dst_ptr = va_arg(ap, long double *); + retval = formatter.scan((long double *)dst_ptr); + write_size = sizeof(long double); + } else if (*(formatter.fmt_cur - 1) == 'l') { + dst_ptr = va_arg(ap, double *); + retval = formatter.scan((double *)dst_ptr); + write_size = sizeof(double); + } else { + dst_ptr = va_arg(ap, float *); + retval = formatter.scan((float *)dst_ptr); + write_size = sizeof(float); + } + l = dfsan_read_label(formatter.str_cur(), + formatter.num_written_bytes(retval)); + if (str_origin == nullptr) + dfsan_set_label(l, dst_ptr, write_size); + else + dfsan_set_label_origin(l, *str_origin, dst_ptr, write_size); + end_fmt = true; + break; + + case 'c': + dst_ptr = va_arg(ap, char *); + retval = formatter.scan((char *)dst_ptr); + write_size = sizeof(char); + l = dfsan_read_label(formatter.str_cur(), + formatter.num_written_bytes(retval)); + if (str_origin == nullptr) + dfsan_set_label(l, dst_ptr, write_size); + else + dfsan_set_label_origin(l, *str_origin, dst_ptr, write_size); + end_fmt = true; + break; + + case 's': { + dst_ptr = va_arg(ap, char *); + retval = formatter.scan((char *)dst_ptr); + if (1 == retval) { + // special case: we have parsed a single string and we need to + // update retval with the string size + retval = strlen((char *)dst_ptr); + } + if (str_origin) { + dfsan_mem_origin_transfer(dst_ptr, formatter.str_cur(), + formatter.num_written_bytes(retval)); + } + va_labels++; + dfsan_mem_shadow_transfer(dst_ptr, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + } + + case 'p': + dst_ptr = va_arg(ap, void *); + retval = + formatter.scan((int *)dst_ptr); // note: changing void* to int* + // since we need to call sizeof + write_size = sizeof(int); + + l = dfsan_read_label(formatter.str_cur(), + formatter.num_written_bytes(retval)); + if (str_origin == nullptr) + dfsan_set_label(l, dst_ptr, write_size); + else + dfsan_set_label_origin(l, *str_origin, dst_ptr, write_size); + end_fmt = true; + break; + + case 'n': { + int *ptr = va_arg(ap, int *); + *ptr = (int)formatter.str_off; + va_labels++; + dfsan_set_label(0, ptr, sizeof(*ptr)); + end_fmt = true; + break; + } + + case '%': + retval = formatter.scan(); + end_fmt = true; + break; + + case '*': + formatter.width = va_arg(ap, int); + va_labels++; + break; + + default: + break; + } + } + } + + if (retval < 0) { + return retval; + } + + formatter.fmt_cur++; + formatter.str_off += retval; + } + + *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, @@ -2390,6 +2750,7 @@ 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); @@ -2438,6 +2799,58 @@ 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(); Index: compiler-rt/lib/dfsan/done_abilist.txt =================================================================== --- compiler-rt/lib/dfsan/done_abilist.txt +++ compiler-rt/lib/dfsan/done_abilist.txt @@ -88,6 +88,7 @@ fun:ispunct=functional fun:isspace=functional fun:tolower=functional +fun:_tolower=functional fun:toupper=functional # Functions that return a value that is data-dependent on the input. @@ -268,6 +269,7 @@ fun:strtoul=custom fun:strtoull=custom fun:strcat=custom +fun:strncat=custom # Functions that produce an output that is computed from the input, but is not # necessarily data dependent. @@ -278,11 +280,13 @@ fun:strchr=custom fun:strcmp=custom fun:strlen=custom +fun:strnlen=custom fun:strncasecmp=custom fun:strncmp=custom fun:strpbrk=custom fun:strrchr=custom fun:strstr=custom +fun:strsep=custom # Functions which take action based on global state, such as running a callback # set by a separate function. @@ -304,6 +308,10 @@ fun:sprintf=custom fun:snprintf=custom +# scanf-like +fun:sscanf=custom +fun:__isoc99_sscanf=custom + # TODO: custom fun:asprintf=discard fun:qsort=discard Index: compiler-rt/test/dfsan/custom.cpp =================================================================== --- compiler-rt/test/dfsan/custom.cpp +++ compiler-rt/test/dfsan/custom.cpp @@ -364,6 +364,47 @@ ASSERT_LABEL(dst[11], j_label); } +void test_strncat() { + char src[] = "world"; + int volatile x = 0; // buffer to ensure src and dst do not share origins + (void)x; + char dst[] = "hello \0 "; + int volatile y = 0; // buffer to ensure dst and p do not share origins + (void)y; + char *p = dst; + dfsan_set_label(k_label, &p, sizeof(p)); + dfsan_set_label(i_label, src, sizeof(src)); + dfsan_set_label(j_label, dst, sizeof(dst)); + dfsan_origin dst_o = dfsan_get_origin((long)dst[0]); + (void)dst_o; + char *ret = strncat(p, src, strlen(src)); + ASSERT_LABEL(ret, k_label); + ASSERT_EQ_ORIGIN(ret, p); + assert(ret == dst); + assert(strcmp(src, dst + 6) == 0); + // Origins are assigned for every 4 contiguous 4-aligned bytes. After + // appending src to dst, origins of src can overwrite origins of dst if their + // application adddresses are within [start_aligned_down, end_aligned_up). + // Other origins are not changed. + char *start_aligned_down = (char *)(((size_t)(dst + 6)) & ~3UL); + char *end_aligned_up = (char *)(((size_t)(dst + 11 + 4)) & ~3UL); + for (int i = 0; i < 12; ++i) { + if (dst + i < start_aligned_down || dst + i >= end_aligned_up) { + ASSERT_INIT_ORIGIN(&dst[i], dst_o); + } else { + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&dst[i], src[0]); + } + } + for (int i = 0; i < 6; ++i) { + ASSERT_LABEL(dst[i], j_label); + } + for (int i = 6; i < strlen(dst); ++i) { + ASSERT_LABEL(dst[i], i_label); + assert(dfsan_get_label(dst[i]) == dfsan_get_label(src[i - 6])); + } + ASSERT_LABEL(dst[11], j_label); +} + void test_strlen() { char str1[] = "str1"; dfsan_set_label(i_label, &str1[3], 1); @@ -378,6 +419,20 @@ #endif } +void test_strnlen() { + char str1[] = "str1"; + dfsan_set_label(i_label, &str1[3], 1); + + int rv = strnlen(str1, 4); + assert(rv == 4); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, i_label); + ASSERT_EQ_ORIGIN(rv, str1[3]); +#endif +} + void test_strdup() { char str1[] = "str1"; dfsan_set_label(i_label, &str1[3], 1); @@ -1627,6 +1682,77 @@ #endif } +void test_strsep() { + char *s = strdup("Hello world/"); + char *delim = strdup(" /"); + + char *p_s = s; + char *base = s; + char *p_delim = delim; + + dfsan_set_label(n_label, p_delim, sizeof(p_delim)); + + char *rv = strsep(&p_s, p_delim); + assert(rv == &base[0]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, n_label); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, *p_delim); +#endif + + dfsan_set_label(m_label, p_s, sizeof(p_s)); + rv = strsep(&p_s, p_delim); + + assert(rv == &base[6]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_LABEL(rv, m_label); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, base[6]); +#else + ASSERT_LABEL(rv, dfsan_union(m_label, n_label)); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, base[6]); +#endif + + free(s); + s = strdup("Hello world/"); + base = s; + free(delim); + delim = strdup(" /"); + p_delim = delim; + dfsan_set_label(i_label, &s[7], 1); + dfsan_set_label(j_label, &delim[0], 1); + + rv = strsep(&s, delim); + assert(rv == &base[0]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(rv); +#else + ASSERT_LABEL(rv, j_label); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, delim[1]); +#endif + + char *ps = s; + dfsan_set_label(dfsan_union(j_label, dfsan_read_label(ps, strlen(ps))), ps, + strlen(ps)); + rv = strsep(&ps, " /"); + assert(rv == &base[6]); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_LABEL(rv, i_j_label); +#else + ASSERT_LABEL(rv, i_j_label); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, base[6]); +#endif + rv = strsep(&ps, " /"); + assert(strlen(rv) == 0); +#ifdef STRICT_DATA_DEPENDENCIES + ASSERT_ZERO_LABEL(ps); +#else + ASSERT_ZERO_LABEL(rv); + ASSERT_INIT_ORIGIN_EQ_ORIGIN(&rv, 0); + +#endif +} + void test_memchr() { char str1[] = "str1"; dfsan_set_label(i_label, &str1[3], 1); @@ -1968,6 +2094,138 @@ ASSERT_LABEL(r, 0); } +template +void test_sscanf_chunk(T expected, const char *format, const char *input) { + char padded_input[512]; + strcpy(padded_input, "foo "); + strcat(padded_input, input); + strcat(padded_input, " bar"); + + char padded_format[512]; + strcpy(padded_format, "foo "); + strcat(padded_format, format); + strcat(padded_format, " bar"); + + // Non labelled arg. + T arg; + memset(&arg, 0, sizeof(arg)); + sscanf(padded_input, padded_format, &arg); + assert(arg == expected); + ASSERT_READ_LABEL(&arg, sizeof(arg), 0); + + // Labelled arg. + memset(&arg, 0, sizeof(arg)); + dfsan_set_label(i_label, (void *)(padded_input + 4), strlen(input)); + dfsan_origin a_o = dfsan_get_origin((long)(padded_input + 4)); +#ifndef ORIGIN_TRACKING + (void)a_o; +#endif + sscanf(padded_input, padded_format, &arg); + assert(arg == expected); + ASSERT_READ_LABEL(&arg, sizeof(arg), i_label); + ASSERT_INIT_ORIGINS(&arg, sizeof(arg), a_o); +} + +void test_sscanf() { + char buf[2048]; + char buf_out[2048]; + memset(buf, 'a', sizeof(buf)); + memset(buf_out, 'a', sizeof(buf_out)); + + // Test formatting + strcpy(buf, "Hello world!"); + assert(sscanf(buf, "%s", buf_out) == 1); + assert(strcmp(buf, "Hello world!") == 0); + assert(strcmp(buf_out, "Hello") == 0); + ASSERT_READ_LABEL(buf, sizeof(buf), 0); + ASSERT_READ_LABEL(buf_out, sizeof(buf_out), 0); + + // Test for extra arguments. + assert(sscanf(buf, "%s", buf_out, 42, "hello") == 1); + assert(strcmp(buf, "Hello world!") == 0); + assert(strcmp(buf_out, "Hello") == 0); + ASSERT_READ_LABEL(buf, sizeof(buf), 0); + ASSERT_READ_LABEL(buf_out, sizeof(buf_out), 0); + + // Test formatting & label propagation (multiple conversion specifiers): %s, + // %d, %n, %f, and %%. + int n; + strcpy(buf, "hello world, 2014/8/27 12345.678123 % 1000"); + char *s = buf + 6; //starts with world + int y = 0; + int m = 0; + int d = 0; + float fval; + int val = 0; + dfsan_set_label(k_label, (void *)(s + 1), 2); + dfsan_origin s_o = dfsan_get_origin((long)(s[1])); + dfsan_set_label(i_label, (void *)(s + 12), 1); + dfsan_origin m_o = dfsan_get_origin((long)m); + dfsan_set_label(j_label, (void *)(s + 14), 2); + dfsan_origin d_o = dfsan_get_origin((long)d); + dfsan_set_label(m_label, (void *)(s + 17), sizeof(fval)); + dfsan_origin f_o = dfsan_get_origin((long)fval); + +#ifndef ORIGIN_TRACKING + (void)s_o; + (void)m_o; + (void)d_o; + (void)f_o; +#endif + + int r = sscanf(buf, "hello %s %d/%d/%d %f %% %n%d", buf_out, &y, &m, &d, + &fval, &n, &val); + assert(r == 6); + assert(strcmp(buf_out, "world,") == 0); + ASSERT_READ_LABEL(buf_out, 1, 0); + ASSERT_READ_LABEL(buf_out + 1, 2, k_label); + ASSERT_INIT_ORIGINS(buf_out + 1, 2, s_o); + ASSERT_READ_LABEL(buf + 9, 9, 0); + ASSERT_READ_LABEL(&m, 4, i_label); + ASSERT_INIT_ORIGINS(&m, 4, m_o); + ASSERT_READ_LABEL(&d, 4, j_label); + ASSERT_INIT_ORIGINS(&d, 4, d_o); + ASSERT_READ_LABEL(&fval, sizeof(fval), m_label); + ASSERT_INIT_ORIGINS(&fval, sizeof(fval), f_o); + ASSERT_READ_LABEL(&val, 4, 0); + ASSERT_LABEL(r, 0); + assert(n == 38); + assert(val == 1000); + + // Test formatting & label propagation (single conversion specifier, with + // additional length and precision modifiers). + test_sscanf_chunk(-559038737, "%d", "-559038737"); + test_sscanf_chunk(3735928559, "%u", "3735928559"); + test_sscanf_chunk(12345, "%i", "12345"); + test_sscanf_chunk(489, "%o", "0751"); + test_sscanf_chunk(47806, "%x", "0xbabe"); + test_sscanf_chunk(47806, "%10X", "0x0000BABE"); + test_sscanf_chunk((char)-17, "%hhd", "3735928559"); + test_sscanf_chunk((short)-16657, "%hd", "3735928559"); + test_sscanf_chunk(0xdeadbeefdeadbeefL, "%lx", "0xdeadbeefdeadbeef"); + test_sscanf_chunk((void *)0xdeadbeefdeadbeefL, "%p", "0xdeadbeefdeadbeef"); + intmax_t _x = (intmax_t)-1; + char _buf[256]; + memset(_buf, 0, sizeof(_buf)); + sprintf(_buf, "%ju", _x); + test_sscanf_chunk((intmax_t)18446744073709551615, "%ju", _buf); + memset(_buf, 0, sizeof(_buf)); + size_t _y = (size_t)-1; + sprintf(_buf, "%zu", _y); + test_sscanf_chunk((size_t)18446744073709551615, "%zu", _buf); + memset(_buf, 0, sizeof(_buf)); + ptrdiff_t _z = (size_t)-1; + sprintf(_buf, "%tu", _z); + test_sscanf_chunk((ptrdiff_t)18446744073709551615, "%tu", _buf); + + test_sscanf_chunk((float)0.123456, "%8f", "0.123456"); + test_sscanf_chunk((float)0.123456, "%g", "0.123456"); + test_sscanf_chunk((float)1.234560e-01, "%e", "0.123456"); + test_sscanf_chunk((char)'z', "%c", "z"); + + // %n, %s, %d, %f, and %% already tested +} + // Tested by a seperate source file. This empty function is here to appease the // check-wrappers script. void test_fork() {} @@ -2029,18 +2287,22 @@ test_snprintf(); test_socketpair(); test_sprintf(); + test_sscanf(); test_stat(); test_strcasecmp(); test_strchr(); test_strcmp(); test_strcat(); + test_strncat(); test_strcpy(); test_strdup(); test_strlen(); + test_strnlen(); test_strncasecmp(); test_strncmp(); test_strncpy(); test_strpbrk(); + test_strsep(); test_strrchr(); test_strstr(); test_strtod();