This change adds the interceptors for strndup and strndup on linux as strndup get generated as strndup at -O3.
Details
- Reviewers
kcc vitalybuka filcab eugenis rnk dvyukov - Commits
- rG183d1368f311: [asan] Add strndup/__strndup interceptors.
rG0550581070ab: [asan] Recommit of r301904: Add strndup/__strndup interceptors
rGb7101479a899: [asan] Add strndup/__strndup interceptors if targeting linux.
rCRT304399: [asan] Add strndup/__strndup interceptors.
rCRT302781: [asan] Recommit of r301904: Add strndup/__strndup interceptors
rCRT301904: [asan] Add strndup/__strndup interceptors if targeting linux.
rL304399: [asan] Add strndup/__strndup interceptors.
rL302781: [asan] Recommit of r301904: Add strndup/__strndup interceptors
rL301904: [asan] Add strndup/__strndup interceptors if targeting linux.
Diff Detail
Event Timeline
lib/asan/asan_interceptors.cc | ||
---|---|---|
574 ↗ | (On Diff #93354) | this needs to go to lib/sanitizer_common/sanitizer_common_interceptors.inc and removed from lib/msan/msan_interceptors.cc |
lib/asan/asan_interceptors.h | ||
---|---|---|
77 ↗ | (On Diff #93354) | This one (the plain strdup) should be intercepted in all SANITIZER_POSIX platforms. And it seems Android might have it: https://android.googlesource.com/platform/bionic/+/donut-release/libc/string/strndup.c |
Thank you Kostya and Filipe for the comments, I have made the following changes:
- Moved proposed interceptors into sanitizer_common_interceptors.inc
- Moved existing strndup msan interceptor into sanitizer_common_interceptors.inc
- Added common function COMMON_INTERCEPTOR_STRNDUP_IMPL
- Added intercept_strndup flag true by default
- Enabled strndup interceptor on POSIX
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
340 | Should the context be __strndup? | |
lib/sanitizer_common/tests/sanitizer_test_utils.h | ||
127 | We should also enter here if defined(__APPLE__) and if defined(__FreeBSD__), I think. I'm not sure we should be taking Android out of the picture, as it seems strndup is available, AFAICT. @kcc/@vitalybuka (or others who might be working on Android) can you confirm if we should expect strndup to be available? | |
test/asan/TestCases/strndup_oob_test.cc | ||
9 ↗ | (On Diff #93675) | I don't think this should require Linux. It should have Windows as unsupported, but should work on all (?) POSIX. |
lib/msan/msan_interceptors.cc | ||
---|---|---|
1339 | I don't like that the code tries to combine two very different implementations in one "common" interceptor - one that calls REAL(strlen) and one that uses malloc directly. Common interceptor should implement the logic of strndup, and call individual sanitizers only to update their specific state - READ_RANGE at the start, and something like COPY_RANGE after. I think the common implementation should use malloc directly. | |
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
325 | this should use strnlen, like the msan interceptor it is replacing | |
lib/sanitizer_common/tests/sanitizer_test_utils.h | ||
127 | Yes, strndup is available on Android. |
Thanks Filipe/Eugenis for the comments, one question I have is, since my change modifies the behaviour of msan strndup interceptor (cf. added msan/strndup.cc test), should I keep the msan change as part of this patch or do you prefer a separate review?
lib/msan/msan_interceptors.cc | ||
---|---|---|
1339 | Makes sense. Will update patch. | |
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
325 | Makes sense thanks. | |
340 | Yes I think strndup is preferred because this is also the case with strdup/strndup in asan_interceptors.cc | |
lib/sanitizer_common/tests/sanitizer_test_utils.h | ||
127 | Sounds good will do. | |
test/asan/TestCases/strndup_oob_test.cc | ||
9 ↗ | (On Diff #93675) | Makes sense thanks! |
Hi Eugenis/Filipe,
I have implemented the requested changes:
- Enabling strndup interceptor on all posix platforms including android
- Unsupport test on win32
- Implemented common logic of strndup in common interceptor and added COMMON_INTERCEPTOR_COPY_STRING
Let me know what you think!
Thanks,
Pierre
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
330 | MSan needs COPY_STRING for correctness. Without out, the destination buffer would be left uninitialized (poisoned). It needs to happen regardless of intercept_strndup. Please add a test for this. | |
345 | Please avoid code duplication. Move the interceptor body to COMMON_INTERCEPTOR_STRNDUP_IMPL | |
349 | Hmm I have a vague recollection of tsan having problems with interceptors calling other interceptors. On the other hand, tsan interceptor for strdup calls REAL(strdup), which ends up in the malloc interceptor. Dmitry? | |
355 | internal_memcpy |
Thanks Eugeny for the comments,
I have made the requested changes:
- call COPY_STRING regardless of intercept_strndup
- modified msan test to check poisoning is preserved
- Moved common implementation into COMMON_INTERCEPTOR_STRNDUP_IMPL
- Use internal_memcpy
LGTM
lib/sanitizer_common/tests/sanitizer_test_utils.h | ||
---|---|---|
127 | maybe simply #if !defined(_MSC_VER) |
Reverted the change in rL301909.
The change seems to cause 2 build failures.
Undeclared __interceptor_malloc in esan_interceptors.cc.
Undeclared strnlen on OSX buildbots.
Attempt to fix build failures:
- add declaration of __interceptor_malloc in esan_interceptors.cc
- replace REAL(strnlen) by internal_strnlen
One of the test I have added (strndup_oob_test.cc) is failing on the s390 linux bot (cf http://lab.llvm.org:8011/builders/clang-s390x-linux/builds/8096) .
I have marked the test as unsupported on s390 for now in rL302789 if that's ok?
strndup_oob_test.cc also fails on armv7l-unknown-linux-gnueabihf (cf http://lab.llvm.org:8011/builders/clang-cmake-thumbv7-a15-full-sh/builds/1521).
I have marked it as unsupported in rL302807 as this is a known issue.
compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
230 ↗ | (On Diff #98598) | Min() is redundant here, as (internal_strnlen(s, size) <= size) |
233 ↗ | (On Diff #98598) | must be "COMMON_INTERCEPTOR_READ_RANGE(ctx, s, Min(copy_length + 1, size)); " (copy_length + 1) is a problem if "s" is not zero terminated |
compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
233 ↗ | (On Diff #98598) | I am going to extend test and create a patch for this. |
- Fix off by one issue using Vitaly's fix.
- Expanded msan and asan unit tests to check for the correct handling of non zero terminated strings.
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
229 | probably should be COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min(size, copy_length + 1)); |
Invert arguments positions in Min call.
lib/sanitizer_common/sanitizer_common_interceptors.inc | ||
---|---|---|
229 | Sounds good, I have updated the patch. |
I don't think the order of the arguments matters here. Vitaly's comment was about READ_STRING vs READ_RANGE.
LGTM
test/asan/TestCases/Posix/strndup_oob_test.cc | ||
---|---|---|
28 | Maybe another simple test // RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clang_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s // When built as C on Linux, strndup is transformed to __strndup. // RUN: %clang_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s // Unwind problem on arm: "main" is missing from the allocation stack trace. // UNSUPPORTED: win32,s390,armv7l-unknown-linux-gnueabihf #include <string.h> char kChars[] = {'f', 'o', 'o'}; int main(int argc, char **argv) { char *copy = strndup(kChars, 3); copy = strndup(kChars, 10); // CHECK: AddressSanitizer: global-buffer-overflow // CHECK: {{.*}}main {{.*}}.cc:[[@LINE-2]] return *copy; } |
I don't like that the code tries to combine two very different implementations in one "common" interceptor - one that calls REAL(strlen) and one that uses malloc directly.
Common interceptor should implement the logic of strndup, and call individual sanitizers only to update their specific state - READ_RANGE at the start, and something like COPY_RANGE after.
I think the common implementation should use malloc directly.