Skip to content

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

Merged
merged 10 commits into from
Nov 24, 2021
Merged

ROX-8450: Extract elf metadata with matcher #535

merged 10 commits into from
Nov 24, 2021

Conversation

c-du
Copy link
Contributor

@c-du c-du commented Nov 19, 2021

  1. Extract metadata from elf executable
  2. Create matcher and populate elf metadata to file data.

@c-du c-du requested review from RTann and connorgorman November 19, 2021 18:34
pkg/elf/elf.go Outdated

// MetaData contains the exacted metadata from ELF file
type MetaData struct {
SoNames []string
Copy link
Collaborator

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment

Copy link
Contributor

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

Copy link
Contributor Author

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)
Copy link
Collaborator

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

Copy link
Contributor Author

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 {
Copy link
Collaborator

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

Copy link
Contributor Author

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) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: GetElfMetadata

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

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

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

return elf.IsElfExecutable(contents), false
}

// NewElfMatcher returns a matcher that matches if and only if any of the passed submatchers does.
Copy link
Collaborator

Choose a reason for hiding this comment

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

copy paste?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, Fixed.

@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: ElfMetadata

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

ElfMetaData *elf.MetaData
}

func (f *FileData) String() string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

debugging?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

@@ -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()
Copy link
Collaborator

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

Copy link
Contributor Author

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.

@c-du c-du requested a review from RTann November 19, 2021 23:43
@c-du c-du marked this pull request as ready for review November 20, 2021 01:11
Copy link
Collaborator

@RTann RTann left a 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))
Copy link
Collaborator

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(...)

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor Author

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)
Copy link
Collaborator

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

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)
Copy link
Collaborator

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

Copy link
Contributor Author

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.

@@ -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{}
Copy link
Collaborator

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(\.[^/.]+)*$`)
Copy link
Collaborator

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?

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 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

Copy link
Contributor Author

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)
Copy link
Collaborator

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?

Copy link
Contributor Author

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
Copy link
Contributor

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))
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor

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

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

pkg/elf/elf.go Outdated
return nil, err
}
return &Metadata{
SoNames: soName,
Copy link
Contributor

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?

Copy link
Contributor Author

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(\.[^/.]+)*$`)
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 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

@c-du c-du requested review from RTann and connorgorman November 23, 2021 23:17
Copy link
Contributor

@connorgorman connorgorman left a comment

Choose a reason for hiding this comment

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

Looks good!

Copy link
Collaborator

@RTann RTann left a 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
Copy link
Collaborator

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))
Copy link
Collaborator

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?

@c-du c-du merged commit 1d4958c into master Nov 24, 2021
@c-du c-du deleted the cong-avm2-elf branch November 24, 2021 01:28
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.

3 participants