Skip to content

Commit

Permalink
USM: ssl: Ignore ELF files for other architectures (#25505)
Browse files Browse the repository at this point in the history
* USM: ssl: Ignore ELF files for other architectures

Ignore ELF files which are not for the architecture we're running on.
Without this, we could end up installing a uprobe on, for example, an
arm64 binary on an amd64 machine, thus corrupting the arm64 instruction
and leading to segmentation faults.

* Use blockList in test

* Use OpenFromAnotherProcess instead of libmmap

* Remove unused libmmap

* Simplify testArch

* Skip test on unsupported platforms

* Format fakessl.c with clang-format

* Add shebang link to script

* Move libs to testdata

So that kmt will copy it to the VMs.

(cherry picked from commit a6c060e)

 Conflicts:
	pkg/network/usm/utils/debugger.go
  • Loading branch information
vitkyrka committed May 24, 2024
1 parent 0eec38d commit 2d6a698
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 0 deletions.
20 changes: 20 additions & 0 deletions pkg/network/usm/ebpf_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -600,6 +601,25 @@ func addHooks(m *manager.Manager, procRoot string, probes []manager.ProbesSelect
}
defer elfFile.Close()

// This only allows amd64 and arm64 and not the 32-bit variants, but that
// is fine since we don't monitor 32-bit applications at all in the shared
// library watcher since compat syscalls aren't supported by the syscall
// trace points. We do actually monitor 32-bit applications for istio and
// nodejs monitoring, but our uprobe hooks only properly support 64-bit
// applications, so there's no harm in rejecting 32-bit applications here.
arch, err := bininspect.GetArchitecture(elfFile)
if err != nil {
return err
}

// Ignore foreign architectures. This can happen when running stuff under
// qemu-user, for example, and installing a uprobe will lead to segfaults
// since the foreign instructions will be patched with the native break
// instruction.
if string(arch) != runtime.GOARCH {
return fmt.Errorf("unspported architecture: %s", arch)
}

symbolsSet := make(common.StringSet)
symbolsSetBestEffort := make(common.StringSet)
for _, singleProbe := range probes {
Expand Down
57 changes: 57 additions & 0 deletions pkg/network/usm/ebpf_ssl_test.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 linux_bpf

package usm

import (
"fmt"
"path/filepath"
"runtime"
"testing"

"github.com/DataDog/datadog-agent/pkg/network/config"
"github.com/DataDog/datadog-agent/pkg/network/protocols/http"
"github.com/DataDog/datadog-agent/pkg/network/protocols/http/testutil"
fileopener "github.com/DataDog/datadog-agent/pkg/network/usm/sharedlibraries/testutil"
"github.com/DataDog/datadog-agent/pkg/network/usm/utils"
"github.com/stretchr/testify/require"
)

func testArch(t *testing.T, arch string) {
cfg := config.New()
cfg.EnableNativeTLSMonitoring = true

if !http.TLSSupported(cfg) {
t.Skip("shared library tracing not supported for this platform")
}

curDir, err := testutil.CurDir()
require.NoError(t, err)

libmmap := filepath.Join(curDir, "testdata", "libmmap")
lib := filepath.Join(libmmap, fmt.Sprintf("libssl.so.%s", arch))

monitor := setupUSMTLSMonitor(t, cfg)
require.NotNil(t, monitor)

cmd, err := fileopener.OpenFromAnotherProcess(t, lib)
require.NoError(t, err)

if arch == runtime.GOARCH {
utils.WaitForProgramsToBeTraced(t, "shared_libraries", cmd.Process.Pid)
} else {
utils.WaitForPathToBeBlocked(t, "shared_libraries", lib)
}
}

func TestArchAmd64(t *testing.T) {
testArch(t, "amd64")
}

func TestArchArm64(t *testing.T) {
testArch(t, "arm64")
}
4 changes: 4 additions & 0 deletions pkg/network/usm/testdata/libmmap/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

clang -shared -fpic fakessl.c -target aarch64-linux-gnu -nostdlib -fuse-ld=lld -Wl,-s -o libssl.so.arm64
clang -shared -fpic fakessl.c -target x86_64 -nostdlib -fuse-ld=lld -Wl,-s -o libssl.so.amd64
27 changes: 27 additions & 0 deletions pkg/network/usm/testdata/libmmap/fakessl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 2024-present Datadog, Inc.

//go:build ignore

// Dummy library with same functions as OpenSSL.

void SSL_read_ex(void) {
}
void SSL_write_ex(void) {
}
void SSL_do_handshake(void) {
}
void SSL_connect(void) {
}
void SSL_set_bio(void) {
}
void SSL_set_fd(void) {
}
void SSL_read(void) {
}
void SSL_write(void) {
}
void SSL_shutdown(void) {
}
Binary file added pkg/network/usm/testdata/libmmap/libssl.so.amd64
Binary file not shown.
Binary file added pkg/network/usm/testdata/libmmap/libssl.so.arm64
Binary file not shown.
20 changes: 20 additions & 0 deletions pkg/network/usm/utils/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ func (d *fileRegistryDebugger) GetTracedPrograms() []TracedProgram {
return all
}

// GetBlockedPathIDs returns a list of PathIdentifiers blocked in the
// registry for the specified program type.
func (d *fileRegistryDebugger) GetBlockedPathIDs(programType string) []PathIdentifier {
d.mux.Lock()
defer d.mux.Unlock()

for _, registry := range d.instances {
if registry.telemetry.programName != programType {
continue
}

registry.m.Lock()
defer registry.m.Unlock()

return registry.blocklistByID.Keys()
}

return nil
}

func init() {
debugger = new(fileRegistryDebugger)
}
16 changes: 16 additions & 0 deletions pkg/network/usm/utils/debugger_testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,19 @@ func WaitForProgramsToBeTraced(t *testing.T, programType string, pid int) {
return false
}, time.Second*5, time.Millisecond*100, "process %v is not traced by %v", pid, programType)
}

// WaitForPathToBeBlocked waits for the path to be blocked from tracing in the
// registry (due to failing activation).
func WaitForPathToBeBlocked(t *testing.T, programType string, path string) {
pathID, err := NewPathIdentifier(path)
require.NoError(t, err)
require.Eventuallyf(t, func() bool {
blocked := debugger.GetBlockedPathIDs(programType)
for _, id := range blocked {
if id == pathID {
return true
}
}
return false
}, time.Second*5, time.Millisecond*100, "path %v is not blocked in %v", path, programType)
}

0 comments on commit 2d6a698

Please sign in to comment.