-
Notifications
You must be signed in to change notification settings - Fork 13
ROX-8450: Extract elf metadata with matcher #535
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
Conversation
c-du
commented
Nov 19, 2021
- Extract metadata from elf executable
- Create matcher and populate elf metadata to file data.
pkg/elf/elf.go
Outdated
|
||
// MetaData contains the exacted metadata from ELF file | ||
type MetaData struct { | ||
SoNames []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment specifying these are .so
file names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just SharedObjects would be a more descriptive name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will keep it as soname because that is what it is. Shared objects have more meaning than soname.
pkg/elf/elf.go
Outdated
func IsElfExecutable(r io.ReaderAt) bool { | ||
var elfBytes = make([]byte, len(elf.ELFMAG)) | ||
|
||
_, err := r.ReadAt(elfBytes, 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's actually remove this check and just leave it up to elf.NewFile
to check the magic number for us
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, should work.
pkg/elf/elf.go
Outdated
) | ||
|
||
// MetaData contains the exacted metadata from ELF file | ||
type MetaData struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: metadata is one word, so Metadata
might be better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
pkg/elf/elf.go
Outdated
} | ||
|
||
// GetElfMetadataData extracts and returns ELF metadata | ||
func GetElfMetadataData(r io.ReaderAt) (*MetaData, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: GetElfMetadata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/matcher/matcher.go
Outdated
@@ -54,6 +55,7 @@ func NewWhiteoutMatcher() Matcher { | |||
type executableMatcher struct{} | |||
|
|||
func (e *executableMatcher) Match(_ string, fi os.FileInfo, _ io.ReaderAt) (matches bool, extract bool) { | |||
// Something |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
pkg/matcher/matcher.go
Outdated
return elf.IsElfExecutable(contents), false | ||
} | ||
|
||
// NewElfMatcher returns a matcher that matches if and only if any of the passed submatchers does. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copy paste?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, Fixed.
pkg/tarutil/tarutil.go
Outdated
@@ -64,6 +66,12 @@ type FileData struct { | |||
Contents []byte | |||
// Executable indicates if the file is executable. | |||
Executable bool | |||
// ElfMetaData contains the dynamic library dependency metadata if the file is in ELF format. | |||
ElfMetaData *elf.MetaData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ElfMetadata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/tarutil/tarutil.go
Outdated
ElfMetaData *elf.MetaData | ||
} | ||
|
||
func (f *FileData) String() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
debugging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
pkg/tarutil/tarutil.go
Outdated
@@ -77,6 +85,7 @@ func ExtractFiles(r io.Reader, filenameMatcher matcher.Matcher) (FilesMap, error | |||
// executableMatcher indicates if the given file is executable | |||
// for the FileData struct. | |||
executableMatcher := matcher.NewExecutableMatcher() | |||
elfMatcher := matcher.NewElfMatcher() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed we didn't add the ELF matcher to the filenameMatcher
(see singletons/requiredfilenames/matcher.go
). Is it not necessary? I guess ELF files are all executable, so there may not be a need. I just want to confirm that the current matchers are sufficient enough to capture all ELF files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I forgot not pull it from the draft because it is in dpkg package. Added it back. It is needed because most libraries do not have execution mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments. Also, this adds functionality which will be used later, but is not used now. I'm wondering if we should add another feature flag to put this behind? Maybe that's overkill
pkg/elf/elf.go
Outdated
} | ||
|
||
// Exclude core and other unknown elf file. | ||
return set.NewFrozenIntSet(int(elf.ET_EXEC), int(elf.ET_DYN)).Contains(int(elfFile.Type)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe make this a global elfTypeAllowlist = set.NewFrozenIntSet(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/elf/elf.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
soName, err := elfFile.DynString(elf.DT_SONAME) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it's usually soname
? Not 100% sure because I just looked it up right now. Not a big deal, though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/tarutil/tarutil.go
Outdated
isElf, _ := elfMatcher.Match(filename, hdr.FileInfo(), contents) | ||
if isElf { | ||
if elfMetadata, err := elf.GetElfMetadata(contents); err != nil { | ||
log.Errorf("Failed to get dependencies for %s", filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you specify this is an ELF file? Failed to get dependencies for elf file %s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added that to inner function and appended the inner error here.
pkg/tarutil/tarutil.go
Outdated
@@ -131,13 +135,21 @@ func ExtractFiles(r io.Reader, filenameMatcher matcher.Matcher) (FilesMap, error | |||
|
|||
// Extract the element | |||
if hdr.Typeflag == tar.TypeSymlink || hdr.Typeflag == tar.TypeLink || hdr.Typeflag == tar.TypeReg { | |||
executable, _ := executableMatcher.Match(filename, hdr.FileInfo(), contents) | |||
fileData := FileData{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: var fileData FileData
once sync.Once | ||
instance matcher.Matcher | ||
once sync.Once | ||
dynamicLibRegexp = regexp.MustCompile(`(^|/)lib[^/.]*\.so(\.[^/.]+)*$`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: dynamicLibPattern
Also, maybe put a newline before this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some tests for this regex that simply take some strings and check that it matches ones we expect to match? This will validate that the regex matches what we expect and also give some examples of what it should match
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
@@ -39,7 +41,9 @@ func SingletonMatcher() matcher.Matcher { | |||
// Therefore, this matcher MUST be the last matcher. | |||
executableMatcher := matcher.NewExecutableMatcher() | |||
|
|||
allMatchers = append(allMatchers, dpkgFilenamesMatcher, executableMatcher) | |||
dynamicLibMatcher := matcher.NewRegexpMatcher(dynamicLibRegexp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's put this one above the executableMatcher
addition and append it before the executableMatcher
. This will be necessary because the executableMatcher
does not tell us to extract the file contents, but the RegexpMatcher
does, and we will need to extract the so file contents (I believe).
Also, can you bump the allMatchers := make([]matcher.Matcher, 0, 4)
to 5?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/elf/elf.go
Outdated
|
||
// MetaData contains the exacted metadata from ELF file | ||
type MetaData struct { | ||
SoNames []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just SharedObjects would be a more descriptive name
pkg/elf/elf.go
Outdated
} | ||
|
||
// Exclude core and other unknown elf file. | ||
return set.NewFrozenIntSet(int(elf.ET_EXEC), int(elf.ET_DYN)).Contains(int(elfFile.Type)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
pkg/elf/elf.go
Outdated
} | ||
soName, err := elfFile.DynString(elf.DT_SONAME) | ||
if err != nil { | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we wrap these errors with what action failed? I worry the internal message may not be clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added that.
pkg/elf/elf.go
Outdated
} | ||
libraries, err := elfFile.ImportedLibraries() | ||
if err != nil { | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we wrap these errors with what action failed? I worry the internal message may not be clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
pkg/elf/elf.go
Outdated
return nil, err | ||
} | ||
return &Metadata{ | ||
SoNames: soName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find it a bit strange we assign a singular variable name to a plural. maybe soNames for the var?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, fixed. elf.DynString sometimes gives me a feeling that this is singular.
once sync.Once | ||
instance matcher.Matcher | ||
once sync.Once | ||
dynamicLibRegexp = regexp.MustCompile(`(^|/)lib[^/.]*\.so(\.[^/.]+)*$`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some tests for this regex that simply take some strings and check that it matches ones we expect to match? This will validate that the regex matches what we expect and also give some examples of what it should match
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM just two nits to consider before merging
pkg/elf/elf.go
Outdated
ImportedLibraries []string | ||
} | ||
|
||
// OpenIfElfExecutable tests if the data is in ELF format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: update the comment to reflect new functionality
pkg/elf/elf.go
Outdated
) | ||
|
||
var ( | ||
allowedElfTypeList = set.NewFrozenIntSet(int(elf.ET_EXEC), int(elf.ET_DYN)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should we do allowedELFTypeList
as well as changing other instances of Elf
with ELF
?