Skip to content
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

feat(lib): use memfd on linux instead of dumping libddwaf.so in /tmp #106

Merged
merged 2 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
16 changes: 8 additions & 8 deletions internal/bindings/waf_dl.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,22 @@ type wafSymbols struct {
run uintptr
}

// newWafDl loads the libddwaf shared library and resolves all tge relevant symbols.
// NewWafDl loads the libddwaf shared library and resolves all tge relevant symbols.
// The caller is responsible for calling wafDl.Close on the returned object once they
// are done with it so that associated resources can be released.
func NewWafDl() (dl *WafDl, err error) {
file, err := lib.DumpEmbeddedWAF()
file, closer, err := lib.DumpEmbeddedWAF()
if err != nil {
return
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we not ignore this err?

Suggested change
return
return nil, fmt.Errorf("write an embedded WAF library: %w", err)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah sh*t. Should have disabled auto-merge when I saw your comments. Don't worry I will do it in another PR

}
defer func() {
if rmErr := os.Remove(file); rmErr != nil {
err = errors.Join(err, fmt.Errorf("error removing %s: %w", file, rmErr))
if rmErr := closer(); rmErr != nil {
err = errors.Join(err, fmt.Errorf("error removing %s: %w", file.Name(), rmErr))
}
}()

var handle uintptr
if handle, err = purego.Dlopen(file, purego.RTLD_GLOBAL|purego.RTLD_NOW); err != nil {
if handle, err = purego.Dlopen(file.Name(), purego.RTLD_GLOBAL|purego.RTLD_NOW); err != nil {
return
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return
return nil, fmt.Errorf("dlopen: %w", err)

}

Expand Down Expand Up @@ -97,12 +97,12 @@ func (waf *WafDl) Close() error {
return purego.Dlclose(waf.handle)
}

// wafGetVersion returned string is a static string so we do not need to free it
// WafGetVersion returned string is a static string so we do not need to free it
func (waf *WafDl) WafGetVersion() string {
return unsafe.Gostring(unsafe.Cast[byte](waf.syscall(waf.getVersion)))
}

// wafInit initializes a new WAF with the provided ruleset, configuration and info objects. A
// WafInit initializes a new WAF with the provided ruleset, configuration and info objects. A
// cgoRefPool ensures that the provided input values are not moved or garbage collected by the Go
// runtime during the WAF call.
func (waf *WafDl) WafInit(ruleset *WafObject, config *WafConfig, info *WafObject) WafHandle {
Expand All @@ -125,7 +125,7 @@ func (waf *WafDl) WafDestroy(handle WafHandle) {
unsafe.KeepAlive(handle)
}

// wafKnownAddresses returns static strings so we do not need to free them
// WafKnownAddresses returns static strings so we do not need to free them
func (waf *WafDl) WafKnownAddresses(handle WafHandle) []string {
var nbAddresses uint32

Expand Down
13 changes: 6 additions & 7 deletions internal/bindings/waf_dl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"debug/elf"
"debug/macho"
"fmt"
"os"
"runtime"
"testing"

Expand All @@ -33,14 +32,14 @@ func TestVerifyHeader(t *testing.T) {
// testVerifyELFHeader is here to ease the debug cases that will likely need
// to dive in the linker to debug because the error handling is very poor
func testVerifyELFHeader(t *testing.T) {
file, err := lib.DumpEmbeddedWAF()
file, closer, err := lib.DumpEmbeddedWAF()
require.NoError(t, err)

defer func() {
_ = os.Remove(file)
_ = closer()
}()

elfFile, err := elf.Open(file)
elfFile, err := elf.Open(file.Name())
require.NoError(t, err)

switch runtime.GOARCH {
Expand All @@ -60,14 +59,14 @@ func testVerifyELFHeader(t *testing.T) {
// testVerifyMachOHeader is here to ease the debug cases that will likely need
// to dive in the linker to debug because the error handling is very poor
func testVerifyMachOHeader(t *testing.T) {
file, err := lib.DumpEmbeddedWAF()
file, closer, err := lib.DumpEmbeddedWAF()
require.NoError(t, err)

defer func() {
_ = os.Remove(file)
_ = closer()
}()

machOFile, err := macho.Open(file)
machOFile, err := macho.Open(file.Name())
require.NoError(t, err)

switch runtime.GOARCH {
Expand Down
57 changes: 57 additions & 0 deletions internal/lib/dump_waf_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build darwin && (amd64 || arm64) && !go1.24 && !datadog.no_waf && (cgo || appsec)

package lib

import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"os"

_ "embed"
)

//go:embed .version
var EmbeddedWAFVersion string

func DumpEmbeddedWAF() (file *os.File, closer func() error, err error) {
file, err = os.CreateTemp("", embedNamePattern)
if err != nil {
return nil, nil, fmt.Errorf("error creating temp file: %w", err)
}

defer func() {
if err != nil {
if closeErr := file.Close(); closeErr != nil {
err = errors.Join(err, fmt.Errorf("error closing file: %w", closeErr))
}
if rmErr := os.Remove(file.Name()); rmErr != nil {
err = errors.Join(err, fmt.Errorf("error removing file: %w", rmErr))
}
}
}()

gr, err := gzip.NewReader(bytes.NewReader(libddwaf))
if err != nil {
return nil, nil, fmt.Errorf("error creating gzip reader: %w", err)
}

if _, err := io.Copy(file, gr); err != nil {
eliottness marked this conversation as resolved.
Show resolved Hide resolved
eliottness marked this conversation as resolved.
Show resolved Hide resolved
return nil, nil, fmt.Errorf("error copying gzip content to file: %w", err)
}

if err := gr.Close(); err != nil {
return nil, nil, fmt.Errorf("error closing gzip reader: %w", err)
}

return file, func() error {
return errors.Join(file.Close(), os.Remove(file.Name()))
}, nil
}
59 changes: 59 additions & 0 deletions internal/lib/dump_waf_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build linux && (amd64 || arm64) && !go1.24 && !datadog.no_waf && (cgo || appsec)

package lib

import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"golang.org/x/sys/unix"
"io"
"os"

_ "embed"
)

//go:embed .version
var EmbeddedWAFVersion string
eliottness marked this conversation as resolved.
Show resolved Hide resolved

func DumpEmbeddedWAF() (file *os.File, closer func() error, err error) {

eliottness marked this conversation as resolved.
Show resolved Hide resolved
fd, err := unix.MemfdCreate("libddwaf", 0)
if err != nil {
return nil, nil, fmt.Errorf("error creating memfd: %w", err)
}

file = os.NewFile(uintptr(fd), fmt.Sprintf("/proc/self/fd/%d", fd))
if file == nil {
return nil, nil, errors.New("error creating file from fd")
}

defer func() {
if file != nil && err != nil {
if closeErr := file.Close(); closeErr != nil {
err = errors.Join(err, fmt.Errorf("error closing file: %w", closeErr))
}
}
}()

gr, err := gzip.NewReader(bytes.NewReader(libddwaf))
if err != nil {
return nil, nil, fmt.Errorf("error creating gzip reader: %w", err)
}

if _, err := io.Copy(file, gr); err != nil {
eliottness marked this conversation as resolved.
Show resolved Hide resolved
eliottness marked this conversation as resolved.
Show resolved Hide resolved
return nil, nil, fmt.Errorf("error copying gzip content to memfd: %w", err)
}

if err := gr.Close(); err != nil {
return nil, nil, fmt.Errorf("error closing gzip reader: %w", err)
}

return file, file.Close, nil
}
61 changes: 0 additions & 61 deletions internal/lib/lib.go

This file was deleted.

Loading