Skip to content

Check PIE binary for linux in Object.kind() #756

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
35 changes: 28 additions & 7 deletions src/read/elf/file.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use alloc::vec::Vec;
use core::convert::TryInto;
use core::fmt::Debug;
use core::mem;

use crate::elf;
use crate::elf::{DF_1_PIE, DT_FLAGS_1};
use crate::endian::{self, Endian, Endianness, U32};
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't import these. Use elf::DF_1_PIE etc.

use crate::pod::Pod;
use crate::read::{
self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object,
ObjectKind, ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex,
};
use alloc::vec::Vec;
use core::convert::TryInto;
use core::fmt::Debug;
use core::mem;

Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't move these.

use super::{
CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection,
Expand Down Expand Up @@ -297,8 +297,29 @@ where
match self.header.e_type(self.endian) {
elf::ET_REL => ObjectKind::Relocatable,
elf::ET_EXEC => ObjectKind::Executable,
// TODO: check for `DF_1_PIE`?
elf::ET_DYN => ObjectKind::Dynamic,
elf::ET_DYN => {
let mut is_pie = false;
let table: &SectionTable<'_, Elf, R> = self.elf_section_table();
if let Ok(Some(dyn_sec)) = table.dynamic(self.endian(), self.data()) {
for v in dyn_sec.0 {
let tag = v.tag32(self.endian());
let val = v.val32(self.endian());
if let Some(tag) = tag {
if let Some(val) = val {
if tag == DT_FLAGS_1 && val & DF_1_PIE == DF_1_PIE {
is_pie = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

LLVM only started setting this flag in 2020: https://reviews.llvm.org/D80872

Any ET_DYN file with non-zero e_entry and/or a PT_INTERP can be run as executable. And it is possible to make a file that is both a valid executable and a valid shared library. For example the dynamic linker on Linux generally is both.

Copy link
Contributor

Choose a reason for hiding this comment

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

So maybe we should replace ObjectKind with methods like is_executable and is_dynamic?

Copy link
Author

Choose a reason for hiding this comment

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

fiy this is how file detects PIE executable
https://stackoverflow.com/questions/34519521/why-does-gcc-create-a-shared-object-instead-of-an-executable-binary-according-to/55704865#55704865

I originally wanted to check the permissions but it's hard to implement it here (i need to know the path)

}
}
}
}
}

if is_pie {
ObjectKind::PIE
} else {
ObjectKind::Dynamic
}
}
elf::ET_CORE => ObjectKind::Core,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain more about how you intend to use this, or even better give a link to the code where you use this. Do you actually need PIE, or would returning Executable suffice?

The question mark on the TODO was because I wasn't sure this is something we actually want. We also need to be careful because existing users may not want this change, so we'll need to review those.

Copy link
Author

Choose a reason for hiding this comment

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

Ok. Actually I need this for addr2line crate of yours

https://github.com/AFLplusplus/LibAFL/blob/main/libafl_qemu/src/modules/utils/addr2line.rs#L111
this is the example.
to look up an address from the Loader struct in addr2line. I need to check for if the binary is PIE executable or not.

  1. if it is PIE executable, i need to use the relative address
    (for example, if your binary is loaded at 0x555555556000 and you want to look for 0x555555556123, then I need to use 0x123 to lookup from the loader)
  2. on the other hand, if it is not a PIE executable, I can just directly use the addres where it was loaded.

To detect this difference is necessary for me to use addr2line

Copy link
Contributor

Choose a reason for hiding this comment

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

So case 1 is ET_DYN+DF_1_PIE, and case 2 is ET_EXEC. How do you need to handle ET_DYN without DF_1_PIE?

_ => ObjectKind::Unknown,
}
Expand Down
2 changes: 2 additions & 0 deletions src/read/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ pub enum ObjectKind {
Executable,
/// Dynamic shared object.
Dynamic,
/// PIE executable
PIE,
/// Core.
Core,
}
Expand Down
Loading