Skip to content

harden ELF relocation parsing and mmap safety#2

Open
Y-0x wants to merge 6 commits into
PerformanC:mainfrom
Y-0x:main
Open

harden ELF relocation parsing and mmap safety#2
Y-0x wants to merge 6 commits into
PerformanC:mainfrom
Y-0x:main

Conversation

@Y-0x
Copy link
Copy Markdown

@Y-0x Y-0x commented May 26, 2026

  • Prevent OOB reads and UB in SLEB128 decoding
  • Add bounds validation during Android relocation unpacking
  • Prevent integer mismatch in relocation count decoding
  • Replace MAP_FIXED with MAP_FIXED_NOREPLACE for safer mappings

Y-0x added 4 commits May 26, 2026 05:59
Fix an Out-of-Bounds (OOB) read bug in sleb128_decode().
- Added early return on buffer overrun.
- Added shift overflow check to prevent UB.
Added boundary check to prevent out of bounds heap write in
elfutil_unpack_android_relocs() This ensures that parsed relocations
do not exceed the pre-allocated buffer size. Also added free()
- Replace MAP_FIXED with MAP_FIXED_NOREPLACE in plti_internal_set_got_entry().
- This prevents unintentional overwriting of existing memory mappings if the
  hint address is already occupied.
- Added a fallback mechanism to allocate memory safely without a hint
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

All Contributors have signed the CLA. The PR is now allowed to be merged.
Posted by the CLA Assistant Lite bot.

@Y-0x
Copy link
Copy Markdown
Author

Y-0x commented May 26, 2026

I have read the CLA Document and I hereby sign the CLA

performanc-bot added a commit to PerformanC/CLA-Signatures that referenced this pull request May 26, 2026
Comment thread src/elf_util.c
Comment on lines +83 to +86
if (decoder->current >= decoder->end) {
LOGF("Failed to decode SLEB128: buffer overrun");
return 0;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

since our LOGF doesn't abort like AOSP's async_safe_fatal, the return 0 is necessary to prevent OOB reads. Do you think we should keep it for safety, or would you prefer a different way to handle this?

Comment thread src/elf_util.c
Comment on lines +91 to +94
if (shift > size) {
LOGF("SLEB128 shift overflow");
return 0;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

^
|

Comment thread src/elf_util.c
Comment on lines +336 to +338
int64_t num_relocs_signed = sleb128_decode(&decoder);
if (num_relocs_signed <= 0) return false;
uint64_t num_relocs = (uint64_t)num_relocs_signed;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'll see about that, but there should be no case where it will ever go below 0

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

If sleb128_decode returns -1 due to corruption
uint64_t num_relocs = (int64_t)(-1); // → 0xFFFFFFFFFFFFFFFF
if (num_relocs <= 0) // unsigned compare: always false
The check silently passes and calloc(0xFFFFFFFFFFFFFFFF, ...) gets called. The type mismatch is the bug regardless of intent.

Comment thread src/elf_util.c
Comment on lines +407 to +411
if (out_index >= num_relocs) {
LOGE("Android reloc: out_index exceeded num_relocs");
free(entries);
return false;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow the project coding style. There should be empty new lines between the LOGE, free and return, moreover the "Android reloc: " shouldn't exist

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Understood, will fix the formatting.

Comment thread src/plti.c Outdated
void *backup_addr = mmap(hint, vma_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
void *backup_addr = MAP_FAILED;
if (hint)
backup_addr = mmap(hint, vma_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

https://man7.org/linux/man-pages/man2/mmap.2.html

MAX_FIXED_NOREPLACE exists only in 4.17+. That would reduce compatibility.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

fair point

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've replaced MAP_FIXED with a fallback approach. By attempting the mmap with a hint first, and falling back to NULL if it fails

@Y-0x
Copy link
Copy Markdown
Author

Y-0x commented May 26, 2026

Thanks for the thorough review and for pointing out the AOSP references. It really makes sense to align with the standard implementation to ensure stability and compatibility, especially regarding the sleb128 logic.
I’m working on incorporating these changes, including the adjustments to mmap to maintain kernel compatibility and following the project's styling guidelines. Appreciate the sharp eye

@Y-0x Y-0x requested a review from ThePedroo May 26, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants