Index: lib/dfsan/dfsan_custom.cc =================================================================== --- lib/dfsan/dfsan_custom.cc +++ lib/dfsan/dfsan_custom.cc @@ -900,6 +900,30 @@ return r; } +// Returns the size of a formatted chunk. +static size_t size_of_chunk(char *str, size_t off, bool has_size, size_t size, + int status) { + if (status < 0) { + return 0; + } + + size_t chunk_size = status; + + // A return value of {v,}snprintf of size or more means that the output was + // truncated. + if (has_size) { + if (off < size) { + if (chunk_size >= size - off) { + chunk_size -= size - off; + } + } else { + chunk_size = 0; + } + } + + return chunk_size; +} + // Formats the input and propagates the input labels to the output. The output // is stored in 'str'. If 'has_size' is true, 'size' bounds the number of // output bytes. 'format' and 'ap' are the format string and the list of @@ -913,15 +937,9 @@ static int format_buffer(char *str, bool has_size, size_t size, const char *format, dfsan_label *va_labels, dfsan_label *ret_label, va_list ap) { - InternalMmapVector chunks(8); size_t off = 0; while (*format) { - chunks.push_back(Chunk()); - Chunk& chunk = chunks.back(); - chunk.ptr = str + off; - chunk.arg = nullptr; - int status = 0; if (*format != '%') { @@ -931,15 +949,18 @@ for (; *format && *format != '%'; ++format, ++format_size) {} status = format_chunk(str, off, has_size, size, format - format_size, format_size); - chunk.label_type = Chunk::NONE; + dfsan_set_label(0, str + off, + size_of_chunk(str, off, has_size, size, status)); } else { // Conversion directive. Consume all the characters until a conversion // specifier or the end of the string. bool end_format = false; -#define FORMAT_CHUNK(t) \ - format_chunk(str, off, has_size, size, format - format_size, \ - format_size + 1, va_arg(ap, t)) - +#define FORMAT_CHUNK(t) \ + format_chunk(str, off, has_size, size, format - format_size, \ + format_size + 1, va_arg(ap, t)) +#define LABEL_CHUNK(l) \ + dfsan_set_label(l, str + off, \ + size_of_chunk(str, off, has_size, size, status)); for (size_t format_size = 1; *++format && !end_format; ++format_size) { switch (*format) { case 'd': @@ -976,7 +997,7 @@ default: status = FORMAT_CHUNK(int); } - chunk.label_type = Chunk::NUMERIC; + LABEL_CHUNK(*va_labels++); end_format = true; break; @@ -993,42 +1014,50 @@ } else { status = FORMAT_CHUNK(double); } - chunk.label_type = Chunk::NUMERIC; + LABEL_CHUNK(*va_labels++); end_format = true; break; case 'c': status = FORMAT_CHUNK(int); - chunk.label_type = Chunk::NUMERIC; + LABEL_CHUNK(*va_labels++); end_format = true; break; - case 's': - chunk.arg = va_arg(ap, char *); + case 's': { + char* chunk_arg = va_arg(ap, char *); status = format_chunk(str, off, has_size, size, format - format_size, format_size + 1, - chunk.arg); - chunk.label_type = Chunk::STRING; + chunk_arg); + va_labels++; + internal_memcpy( + shadow_for(str + off), shadow_for(chunk_arg), + sizeof(dfsan_label) * + (size_of_chunk(str, off, has_size, size, status))); end_format = true; break; + } case 'p': status = FORMAT_CHUNK(void *); - chunk.label_type = Chunk::NUMERIC; + LABEL_CHUNK(*va_labels++); end_format = true; break; - case 'n': - *(va_arg(ap, int *)) = (int)off; - chunk.label_type = Chunk::IGNORED; + case 'n': { + int* chunk_ptr = va_arg(ap, int *); + *chunk_ptr = (int)off; + va_labels++; + dfsan_set_label(0, chunk_ptr, sizeof(chunk_ptr)); end_format = true; break; + } case '%': status = format_chunk(str, off, has_size, size, format - format_size, format_size + 1); - chunk.label_type = Chunk::NONE; + LABEL_CHUNK(0); end_format = true; break; @@ -1036,6 +1065,7 @@ break; } } +#undef LABEL_CHUNK #undef FORMAT_CHUNK } @@ -1043,56 +1073,9 @@ return status; } - // A return value of {v,}snprintf of size or more means that the output was - // truncated. - if (has_size) { - if (off < size) { - size_t ustatus = (size_t) status; - chunk.size = ustatus >= (size - off) ? - ustatus - (size - off) : ustatus; - } else { - chunk.size = 0; - } - } else { - chunk.size = status; - } off += status; } - // TODO(martignlo): Decide how to combine labels (e.g., whether to ignore or - // not the label of the format string). - - // Label each output chunk according to the label supplied as argument to the - // function. We need to go through all the chunks and arguments even if the - // string was only partially printed ({v,}snprintf case). - for (size_t i = 0; i < chunks.size(); ++i) { - const Chunk& chunk = chunks[i]; - void *chunk_ptr = const_cast(chunk.ptr); - - switch (chunk.label_type) { - case Chunk::NONE: - dfsan_set_label(0, chunk_ptr, chunk.size); - break; - case Chunk::IGNORED: - va_labels++; - dfsan_set_label(0, chunk_ptr, chunk.size); - break; - case Chunk::NUMERIC: { - dfsan_label label = *va_labels++; - dfsan_set_label(label, chunk_ptr, chunk.size); - break; - } - case Chunk::STRING: { - // Consume the label of the pointer to the string - va_labels++; - internal_memcpy(shadow_for(chunk_ptr), - shadow_for(chunk.arg), - sizeof(dfsan_label) * (strlen(chunk.arg))); - break; - } - } - } - *ret_label = 0; // Number of bytes written in total. Index: test/dfsan/custom.cc =================================================================== --- test/dfsan/custom.cc +++ test/dfsan/custom.cc @@ -443,6 +443,40 @@ ASSERT_READ_ZERO_LABEL(&usage, sizeof(usage)); } +void test_strcat() { + char src[] = "world"; + char dst[512]; + strcpy(dst, "hello "); + + dfsan_set_label(i_label, dst + 3, 5); + dfsan_set_label(j_label, src + 1, 3); + char *ret = strcat(dst, src); + assert(ret == dst); + assert(strcmp(dst, "hello world") == 0); + ASSERT_READ_ZERO_LABEL(dst, 3); + ASSERT_READ_LABEL(dst + 3, 3, i_label); + ASSERT_READ_ZERO_LABEL(dst + 6, 1); + ASSERT_READ_LABEL(dst + 7, 3, j_label); + ASSERT_READ_ZERO_LABEL(dst + 10, 2); +} + +void test_strncat() { + char src[] = "world"; + char dst[512]; + strcpy(dst, "hello "); + + dfsan_set_label(i_label, dst + 3, 5); + dfsan_set_label(j_label, src + 1, 3); + char *ret = strncat(dst, src, 3); + assert(ret == dst); + assert(strcmp(dst, "hello wor") == 0); + ASSERT_READ_ZERO_LABEL(dst, 3); + ASSERT_READ_LABEL(dst + 3, 3, i_label); + ASSERT_READ_ZERO_LABEL(dst + 6, 1); + ASSERT_READ_LABEL(dst + 7, 2, j_label); + ASSERT_READ_ZERO_LABEL(dst + 9, 1); +} + void test_strcpy() { char src[] = "hello world"; char dst[sizeof(src) + 2]; @@ -813,11 +847,6 @@ assert(strcmp(buf, "Hello world!") == 0); ASSERT_READ_LABEL(buf, sizeof(buf), 0); - // Test for extra arguments. - assert(sprintf(buf, "Hello world!", 42, "hello") == 12); - assert(strcmp(buf, "Hello world!") == 0); - ASSERT_READ_LABEL(buf, sizeof(buf), 0); - // Test formatting & label propagation (multiple conversion specifiers): %s, // %d, %n, %f, and %%. const char* s = "world";