Skip to content

Commit 8ff3017

Browse files
author
Advenam Tacet
committed
[compiler-rt][ASan] Add function moving annotations
This PR adds a `__sanitizer_move_contiguous_container_annotations` function, which moves annotations from one memory area to another. Old area is unpoisoned at the end. New area is annotated in the same way as the old region at the beginning (within limitations of ASan). ```cpp void __sanitizer_move_contiguous_container_annotations( const void *old_storage_beg_p, const void *old_storage_end_p, const void *new_storage_beg_p, const void *new_storage_end_p) { ``` This function aims to help with short string annotations and similar container annotations. Right now we change trait types of `std::basic_string` when compiling with ASan. https://github.com/llvm/llvm-project/blob/87f3407856e61a73798af4e41b28bc33b5bf4ce6/libcxx/include/string#L738-L751 The goal is to not change `__trivially_relocatable` when compiling with ASan. If this function is accpeted and upstreamed, the next step is creating function like `__memcpy_with_asan` moving memory with ASan. And then using this function instead of `__builtin__memcpy` while moving trivially relocatable objects. NOTICE: I did not test it yet, so it's probably not compiling, but I don't expect big changes. PR is WIP until I test it. --- I'm thinking if there is a good way to address fact that in a container the new buffer is usually bigger than the previous one. We may add two more arguments to the functions to address it (the beginning and the end of the whole buffer. Another potential change is removing `new_storage_end_p` as it's redundant, because we require the same size.
1 parent 95f208f commit 8ff3017

File tree

9 files changed

+358
-0
lines changed

9 files changed

+358
-0
lines changed

compiler-rt/include/sanitizer/common_interface_defs.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,30 @@ void SANITIZER_CDECL __sanitizer_annotate_double_ended_contiguous_container(
193193
const void *old_container_beg, const void *old_container_end,
194194
const void *new_container_beg, const void *new_container_end);
195195

196+
/// Moves annotation from one storage to another.
197+
/// At the end, new buffer is annotated in the same way as old buffer at
198+
/// the very beginning. Old buffer is fully unpoisoned.
199+
/// Main purpose of that function is use while moving trivially relocatable
200+
/// objects, which memory may be poisoned (therefore not trivially with ASan).
201+
///
202+
/// A contiguous container is a container that keeps all of its elements
203+
/// in a contiguous region of memory. The container owns the region of memory
204+
/// <c>[old_storage_beg, old_storage_end)</c> and
205+
/// <c>[new_storage_beg, new_storage_end)</c>;
206+
/// There is no requirement where objects are kept.
207+
/// Poisoned and non-poisoned memory areas can alternate,
208+
/// there are no shadow memory restrictions.
209+
///
210+
/// Argument requirements:
211+
/// New containert has to have the same size as the old container.
212+
/// \param old_storage_beg Beginning of the old container region.
213+
/// \param old_storage_end End of the old container region.
214+
/// \param new_storage_beg Beginning of the new container region.
215+
/// \param new_storage_end End of the new container region.
216+
void SANITIZER_CDECL __sanitizer_move_contiguous_container_annotations(
217+
const void *old_storage_beg, const void *old_storage_end,
218+
const void *new_storage_beg, const void *new_storage_end);
219+
196220
/// Returns true if the contiguous container <c>[beg, end)</c> is properly
197221
/// poisoned.
198222
///

compiler-rt/lib/asan/asan_errors.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,20 @@ void ErrorBadParamsToAnnotateDoubleEndedContiguousContainer::Print() {
354354
ReportErrorSummary(scariness.GetDescription(), stack);
355355
}
356356

357+
void ErrorBadParamsToMoveContiguousContainerAnnotations::Print() {
358+
Report(
359+
"ERROR: AddressSanitizer: bad parameters to "
360+
"__sanitizer_move_contiguous_container_annotations:\n"
361+
" old_storage_beg : %p\n"
362+
" old_storage_end : %p\n"
363+
" new_storage_beg : %p\n"
364+
" new_storage_end : %p\n",
365+
(void *)old_storage_beg, (void *)old_storage_end, (void *)new_storage_beg,
366+
(void *)new_storage_end);
367+
stack->Print();
368+
ReportErrorSummary(scariness.GetDescription(), stack);
369+
}
370+
357371
void ErrorODRViolation::Print() {
358372
Decorator d;
359373
Printf("%s", d.Error());

compiler-rt/lib/asan/asan_errors.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,24 @@ struct ErrorBadParamsToAnnotateDoubleEndedContiguousContainer : ErrorBase {
353353
void Print();
354354
};
355355

356+
struct ErrorBadParamsToMoveContiguousContainerAnnotations : ErrorBase {
357+
const BufferedStackTrace *stack;
358+
uptr old_storage_beg, old_storage_end, new_storage_beg, new_storage_end;
359+
360+
ErrorBadParamsToMoveContiguousContainerAnnotations() = default; // (*)
361+
ErrorBadParamsToMoveContiguousContainerAnnotations(
362+
u32 tid, BufferedStackTrace *stack_, uptr old_storage_beg_,
363+
uptr old_storage_end_, uptr new_storage_beg_, uptr new_storage_end_)
364+
: ErrorBase(tid, 10,
365+
"bad-__sanitizer_annotate_double_ended_contiguous_container"),
366+
stack(stack_),
367+
old_storage_beg(old_storage_beg_),
368+
old_storage_end(old_storage_end_),
369+
new_storage_beg(new_storage_beg_),
370+
new_storage_end(new_storage_end_) {}
371+
void Print();
372+
};
373+
356374
struct ErrorODRViolation : ErrorBase {
357375
__asan_global global1, global2;
358376
u32 stack_id1, stack_id2;
@@ -421,6 +439,7 @@ struct ErrorGeneric : ErrorBase {
421439
macro(StringFunctionSizeOverflow) \
422440
macro(BadParamsToAnnotateContiguousContainer) \
423441
macro(BadParamsToAnnotateDoubleEndedContiguousContainer) \
442+
macro(BadParamsToMoveContiguousContainerAnnotations) \
424443
macro(ODRViolation) \
425444
macro(InvalidPointerPair) \
426445
macro(Generic)

compiler-rt/lib/asan/asan_poisoning.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,134 @@ void __sanitizer_annotate_double_ended_contiguous_container(
576576
}
577577
}
578578

579+
// This function moves annotation from one buffer to another.
580+
// Old buffer is unpoisoned at the end.
581+
void __sanitizer_move_contiguous_container_annotations(
582+
const void *old_storage_beg_p, const void *old_storage_end_p,
583+
const void *new_storage_beg_p, const void *new_storage_end_p) {
584+
if (!flags()->detect_container_overflow)
585+
return;
586+
587+
VPrintf(2, "contiguous_container_old: %p %p\n", old_storage_beg_p,
588+
old_storage_end_p);
589+
VPrintf(2, "contiguous_container_new: %p %p\n", new_storage_beg_p,
590+
new_storage_end_p);
591+
592+
uptr old_storage_beg = reinterpret_cast<uptr>(old_storage_beg_p);
593+
uptr old_storage_end = reinterpret_cast<uptr>(old_storage_end_p);
594+
uptr new_storage_beg = reinterpret_cast<uptr>(new_storage_beg_p);
595+
uptr new_storage_end = reinterpret_cast<uptr>(new_storage_end_p);
596+
597+
constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
598+
599+
if (!(old_storage_beg <= old_storage_end) ||
600+
!(new_storage_beg <= new_storage_end) ||
601+
(old_storage_end - old_storage_beg) !=
602+
(new_storage_end - new_storage_beg)) {
603+
GET_STACK_TRACE_FATAL_HERE;
604+
ReportBadParamsToMoveContiguousContainerAnnotations(
605+
old_storage_beg, old_storage_end, new_storage_beg, new_storage_end,
606+
&stack);
607+
}
608+
609+
uptr new_internal_beg = RoundUpTo(new_storage_beg, granularity);
610+
uptr old_internal_beg = RoundUpTo(old_storage_beg, granularity);
611+
uptr new_external_beg = RoundDownTo(new_storage_beg, granularity);
612+
uptr old_external_beg = RoundDownTo(old_storage_beg, granularity);
613+
uptr new_internal_end = RoundDownTo(new_storage_end, granularity);
614+
uptr old_internal_end = RoundDownTo(old_storage_end, granularity);
615+
616+
// At the very beginning we poison the whole buffer.
617+
// Later we unpoison what is necessary.
618+
PoisonShadow(new_internal_beg, new_internal_end - new_internal_beg,
619+
kAsanContiguousContainerOOBMagic);
620+
if (new_internal_beg != new_storage_beg) {
621+
uptr new_unpoisoned = *(u8 *)MemToShadow(new_external_beg);
622+
if (new_unpoisoned > (new_storage_beg - new_external_beg)) {
623+
*(u8 *)MemToShadow(new_external_beg) =
624+
static_cast<u8>(new_storage_beg - new_external_beg);
625+
}
626+
}
627+
if (new_internal_end != new_storage_end) {
628+
uptr new_unpoisoned = *(u8 *)MemToShadow(new_internal_end);
629+
if (new_unpoisoned <= (new_storage_end - new_internal_end)) {
630+
*(u8 *)MemToShadow(new_external_beg) =
631+
static_cast<u8>(kAsanContiguousContainerOOBMagic);
632+
}
633+
}
634+
635+
// There are two cases.
636+
// 1) Distance between buffers is granule-aligned.
637+
// 2) It's not aligned, that case is slower.
638+
if (old_storage_beg % granularity == new_storage_beg % granularity) {
639+
// When buffers are aligned in the same way, we can just copy shadow memory,
640+
// except first and last granule.
641+
__builtin_memcpy((u8 *)MemToShadow(new_internal_beg),
642+
(u8 *)MemToShadow(old_internal_beg),
643+
(new_internal_end - new_internal_beg) / granularity);
644+
// In first granule we cannot poison anything before beginning of the
645+
// container.
646+
if (new_internal_beg != new_storage_beg) {
647+
uptr old_unpoisoned = *(u8 *)MemToShadow(old_external_beg);
648+
uptr new_unpoisoned = *(u8 *)MemToShadow(new_external_beg);
649+
650+
if (old_unpoisoned > old_storage_beg - old_external_beg) {
651+
*(u8 *)MemToShadow(new_external_beg) = old_unpoisoned;
652+
} else if (new_unpoisoned > new_storage_beg - new_external_beg) {
653+
*(u8 *)MemToShadow(new_external_beg) =
654+
new_storage_beg - new_external_beg;
655+
}
656+
}
657+
// In last granule we cannot poison anything after the end of the container.
658+
if (new_internal_end != new_storage_end) {
659+
uptr old_unpoisoned = *(u8 *)MemToShadow(old_internal_end);
660+
uptr new_unpoisoned = *(u8 *)MemToShadow(new_internal_end);
661+
if (new_unpoisoned <= new_storage_end - new_internal_end &&
662+
old_unpoisoned < new_unpoisoned) {
663+
*(u8 *)MemToShadow(new_internal_end) = old_unpoisoned;
664+
}
665+
}
666+
} else {
667+
// If buffers are not aligned, we have to go byte by byte.
668+
uptr old_ptr = old_storage_beg;
669+
uptr new_ptr = new_storage_beg;
670+
uptr next_new;
671+
for (; new_ptr + granularity <= new_storage_end;) {
672+
next_new = RoundUpTo(new_ptr + 1, granularity);
673+
uptr unpoison_to = 0;
674+
for (; new_ptr != next_new; ++new_ptr, ++old_ptr) {
675+
if (!AddressIsPoisoned(old_ptr)) {
676+
unpoison_to = new_ptr + 1;
677+
}
678+
}
679+
if (unpoison_to != 0) {
680+
uptr granule_beg = new_ptr - granularity;
681+
uptr value = unpoison_to - granule_beg;
682+
*(u8 *)MemToShadow(granule_beg) = static_cast<u8>(value);
683+
}
684+
}
685+
// Only case left is the end of the container in the middle of a granule.
686+
// If memory after the end is unpoisoned, we cannot change anything.
687+
// But if it's poisoned, we should unpoison as little as possible.
688+
if (new_ptr != new_storage_end && AddressIsPoisoned(new_storage_end)) {
689+
uptr unpoison_to = 0;
690+
for (; new_ptr != new_storage_end; ++new_ptr, ++old_ptr) {
691+
if (!AddressIsPoisoned(old_ptr)) {
692+
unpoison_to = new_ptr + 1;
693+
}
694+
}
695+
if (unpoison_to != 0) {
696+
uptr granule_beg = RoundDownTo(new_storage_end, granularity);
697+
uptr value = unpoison_to - granule_beg;
698+
*(u8 *)MemToShadow(granule_beg) = static_cast<u8>(value);
699+
}
700+
}
701+
}
702+
703+
__asan_unpoison_memory_region((void *)old_storage_beg,
704+
old_storage_end - old_storage_beg);
705+
}
706+
579707
static const void *FindBadAddress(uptr begin, uptr end, bool poisoned) {
580708
CHECK_LE(begin, end);
581709
constexpr uptr kMaxRangeToCheck = 32;

compiler-rt/lib/asan/asan_report.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,16 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer(
367367
in_report.ReportError(error);
368368
}
369369

370+
void ReportBadParamsToMoveContiguousContainerAnnotations(
371+
uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg,
372+
uptr new_storage_end, BufferedStackTrace *stack) {
373+
ScopedInErrorReport in_report;
374+
ErrorBadParamsToMoveContiguousContainerAnnotations error(
375+
GetCurrentTidOrInvalid(), stack, old_storage_beg, old_storage_end,
376+
new_storage_beg, new_storage_end);
377+
in_report.ReportError(error);
378+
}
379+
370380
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
371381
const __asan_global *g2, u32 stack_id2) {
372382
ScopedInErrorReport in_report;

compiler-rt/lib/asan/asan_report.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer(
8888
uptr storage_beg, uptr storage_end, uptr old_container_beg,
8989
uptr old_container_end, uptr new_container_beg, uptr new_container_end,
9090
BufferedStackTrace *stack);
91+
void ReportBadParamsToMoveContiguousContainerAnnotations(
92+
uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg,
93+
uptr new_storage_end, BufferedStackTrace *stack);
9194

9295
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
9396
const __asan_global *g2, u32 stack_id2);

compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
1111
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
1212
INTERFACE_FUNCTION(__sanitizer_annotate_double_ended_contiguous_container)
13+
INTERFACE_FUNCTION(__sanitizer_move_contiguous_container_annotations)
1314
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
1415
INTERFACE_FUNCTION(
1516
__sanitizer_double_ended_contiguous_container_find_bad_address)

compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ void __sanitizer_annotate_double_ended_contiguous_container(
7171
const void *old_container_beg, const void *old_container_end,
7272
const void *new_container_beg, const void *new_container_end);
7373
SANITIZER_INTERFACE_ATTRIBUTE
74+
void __sanitizer_move_contiguous_container_annotations(
75+
const void *old_storage_beg, const void *old_storage_end,
76+
const void *new_storage_beg, const void *new_storage_end);
77+
SANITIZER_INTERFACE_ATTRIBUTE
7478
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
7579
const void *end);
7680
SANITIZER_INTERFACE_ATTRIBUTE

0 commit comments

Comments
 (0)