Skip to content

[asan] Re-exec without ASLR if needed on 32-bit Linux #131975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler-rt/lib/asan/asan_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void ReplaceSystemMalloc();
uptr FindDynamicShadowStart();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
void TryReExecWithoutASLR();

// Unpoisons platform-specific stacks.
// Returns true if all stacks have been unpoisoned.
Expand Down
32 changes: 32 additions & 0 deletions compiler-rt/lib/asan/asan_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# include <pthread.h>
# include <stdio.h>
# include <sys/mman.h>
# include <sys/personality.h>
# include <sys/resource.h>
# include <sys/syscall.h>
# include <sys/time.h>
Expand Down Expand Up @@ -107,6 +108,37 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
}

void ReExecWithoutASLR() {
// ASLR personality check.
// Caution: 'personality' is sometimes forbidden by sandboxes, so only call
// this function as a last resort (when the memory mapping is incompatible
// and ASan would fail anyway).
int old_personality = personality(0xffffffff);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have

if (old_personality == -1) {
  [log error]
  return;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (old_personality == -1) {
VReport(1, "WARNING: unable to run personality check.\n");
return;
}

bool aslr_on = (old_personality & ADDR_NO_RANDOMIZE) == 0;

if (aslr_on) {
// Disable ASLR if the memory layout was incompatible.
// Alternatively, we could just keep re-execing until we get lucky
// with a compatible randomized layout, but the risk is that if it's
// not an ASLR-related issue, we will be stuck in an infinite loop of
// re-execing (unless we change ReExec to pass a parameter of the
// number of retries allowed.)
VReport(1,
"WARNING: AddressSanitizer: memory layout is incompatible, "
"possibly due to high-entropy ASLR.\n"
"Re-execing with fixed virtual address space.\n"
"N.B. reducing ASLR entropy is preferable.\n");
CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);

ReExec();
}
}

# if SANITIZER_ANDROID
// FIXME: should we do anything for Android?
void AsanCheckDynamicRTPrereqs() {}
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/asan/asan_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ uptr FindDynamicShadowStart() {
GetMmapGranularity());
}

// Not used.
void TryReExecWithoutASLR() {}

// No-op. Mac does not support static linkage anyway.
void AsanCheckDynamicRTPrereqs() {}

Expand Down
7 changes: 7 additions & 0 deletions compiler-rt/lib/asan/asan_shadow_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ void InitializeShadowMemory() {
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
} else {
// The shadow mappings can shadow the entire user address space. However,
// on 32-bit systems, the maximum ASLR entropy (currently up to 16-bits
// == 256MB) is a significant chunk of the address space; reclaiming it by
// disabling ASLR might allow chonky binaries to run.
if (sizeof(uptr) == 32)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? From what I can see, uptr is an alias of uintptr_t, meaning sizeof is 4 on 32bit systems and this branch is never taken on any platform.

(Yes, I'm late.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...oh, #132682 deleted that check. This question no longer matters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can see, uptr is an alias of uintptr_t, meaning sizeof is 4 on 32bit systems and this branch is never taken on any platform.

You're right, that was a bug. Nice catch :-)

TryReExecWithoutASLR();

Report(
"Shadow memory range interleaves with an existing memory mapping. "
"ASan cannot proceed correctly. ABORTING.\n");
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/asan/asan_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ uptr FindDynamicShadowStart() {
GetMmapGranularity());
}

// Not used
void TryReExecWithoutASLR() {}

void AsanCheckDynamicRTPrereqs() {}

void AsanCheckIncompatibleRT() {}
Expand Down