diff --git a/compiler-rt/lib/dfsan/dfsan_custom.cpp b/compiler-rt/lib/dfsan/dfsan_custom.cpp --- a/compiler-rt/lib/dfsan/dfsan_custom.cpp +++ b/compiler-rt/lib/dfsan/dfsan_custom.cpp @@ -682,6 +682,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); diff --git a/compiler-rt/lib/dfsan/done_abilist.txt b/compiler-rt/lib/dfsan/done_abilist.txt --- a/compiler-rt/lib/dfsan/done_abilist.txt +++ b/compiler-rt/lib/dfsan/done_abilist.txt @@ -268,6 +268,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. diff --git a/compiler-rt/test/dfsan/custom.cpp b/compiler-rt/test/dfsan/custom.cpp --- a/compiler-rt/test/dfsan/custom.cpp +++ b/compiler-rt/test/dfsan/custom.cpp @@ -340,6 +340,7 @@ dfsan_origin dst_o = dfsan_get_origin((long)dst[0]); (void)dst_o; char *ret = strcat(p, src); + ASSERT_LABEL(ret, k_label); ASSERT_EQ_ORIGIN(ret, p); assert(ret == dst); @@ -367,6 +368,56 @@ ASSERT_LABEL(dst[11], j_label); } +void test_strncat(int n) { + 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, n); + + ASSERT_LABEL(ret, k_label); + ASSERT_EQ_ORIGIN(ret, p); + assert(ret == dst); + assert(strncmp(src, dst + 6, n) == 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. + int pad = n % 4; + if (pad) + pad = 4 - pad; + + char *start_aligned_down = (char *)(((size_t)(dst + 6)) & ~3UL); + char *end_aligned_up = (char *)(((size_t)(dst + 6 + n + pad)) & ~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 < 6 + n; ++i) { + ASSERT_LABEL(dst[i], i_label); + assert(dfsan_get_label(dst[i]) == dfsan_get_label(src[i - 6])); + } + for (int i = 6 + n; i < strlen(dst); ++i) { + ASSERT_LABEL(dst[i], j_label); + } + ASSERT_LABEL(dst[11], j_label); +} + void test_strlen() { char str1[] = "str1"; dfsan_set_label(i_label, &str1[3], 1); @@ -2110,6 +2161,8 @@ test_strchr(); test_strcmp(); test_strcat(); + test_strncat(5); + test_strncat(2); test_strcpy(); test_strdup(); test_strlen();