Skip to content

Commit

Permalink
internal: fix reading auxv on 32-bit platforms
Browse files Browse the repository at this point in the history
It turns out that the auxiliary vector has a platform specific size.
Adjust the code to use uintptr to approximate "unsigned long" from C.

Fixes cilium#1133

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
  • Loading branch information
lmb committed Oct 2, 2023
1 parent 7333aa6 commit c41af06
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
44 changes: 36 additions & 8 deletions internal/vdso.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"math"
"os"
"unsafe"

"github.com/cilium/ebpf/internal/unix"
)
Expand All @@ -16,6 +17,8 @@ var (
errAuxvNoVDSO = errors.New("no vdso address found in auxv")
)

const uintptrIs32bits = unsafe.Sizeof((uintptr)(0)) == 4

// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library
// linked into the current process image.
func vdsoVersion() (uint32, error) {
Expand All @@ -31,7 +34,7 @@ func vdsoVersion() (uint32, error) {
}
defer av.Close()

vdsoAddr, err := vdsoMemoryAddress(av)
vdsoAddr, err := vdsoMemoryAddress(av, NativeEndian)
if err != nil {
return 0, fmt.Errorf("finding vDSO memory address: %w", err)
}
Expand All @@ -52,26 +55,51 @@ func vdsoVersion() (uint32, error) {
return c, nil
}

type auxvPair32 struct {
Tag, Value uint32
}

type auxvPair64 struct {
Tag, Value uint64
}

func readAuxvPair(r io.Reader, order binary.ByteOrder) (tag, value uint64, _ error) {
if uintptrIs32bits {
var aux auxvPair32
if err := binary.Read(r, order, &aux); err != nil {
return 0, 0, fmt.Errorf("reading auxv entry: %w", err)
}
return uint64(aux.Tag), uint64(aux.Value), nil
}

var aux auxvPair64
if err := binary.Read(r, order, &aux); err != nil {
return 0, 0, fmt.Errorf("reading auxv entry: %w", err)
}
return aux.Tag, aux.Value, nil
}

// vdsoMemoryAddress returns the memory address of the vDSO library
// linked into the current process image. r is an io.Reader into an auxv blob.
func vdsoMemoryAddress(r io.Reader) (uint64, error) {
func vdsoMemoryAddress(r io.Reader, order binary.ByteOrder) (uintptr, error) {
// See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h
const (
_AT_NULL = 0 // End of vector
_AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image
)

// Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`,
// the address of a page containing the virtual Dynamic Shared Object (vDSO).
aux := struct{ Tag, Val uint64 }{}
for {
if err := binary.Read(r, NativeEndian, &aux); err != nil {
return 0, fmt.Errorf("reading auxv entry: %w", err)
tag, value, err := readAuxvPair(r, order)
if err != nil {
return 0, err
}

switch aux.Tag {
switch tag {
case _AT_SYSINFO_EHDR:
if aux.Val != 0 {
return aux.Val, nil
if value != 0 {
return uintptr(value), nil
}
return 0, fmt.Errorf("invalid vDSO address in auxv")
// _AT_NULL is always the last tag/val pair in the aux vector
Expand Down
30 changes: 25 additions & 5 deletions internal/vdso_test.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,63 @@
package internal

import (
"encoding/binary"
"errors"
"os"
"testing"

qt "github.com/frankban/quicktest"
)

func TestAuxvVDSOMemoryAddress(t *testing.T) {
av, err := os.Open("../testdata/auxv.bin")
if uintptrIs32bits {
t.Skip("System pointer size is not 8 bytes")
}

av, err := os.Open("../testdata/auxv64le.bin")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { av.Close() })

addr, err := vdsoMemoryAddress(av)
addr, err := vdsoMemoryAddress(av, binary.LittleEndian)
if err != nil {
t.Fatal(err)
}

expected := uint64(0x7ffd377e5000)
if addr != expected {
if uint64(addr) != expected {
t.Errorf("Expected vDSO memory address %x, got %x", expected, addr)
}
}

func TestAuxvNoVDSO(t *testing.T) {
if uintptrIs32bits {
t.Skip("System pointer size is not 8 bytes")
}

// Copy of auxv.bin with the vDSO pointer removed.
av, err := os.Open("../testdata/auxv_no_vdso.bin")
av, err := os.Open("../testdata/auxv64le_no_vdso.bin")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { av.Close() })

_, err = vdsoMemoryAddress(av)
_, err = vdsoMemoryAddress(av, binary.LittleEndian)
if want, got := errAuxvNoVDSO, err; !errors.Is(got, want) {
t.Fatalf("expected error '%v', got: %v", want, got)
}
}

func TestProcAuxv(t *testing.T) {
av, err := os.Open("/proc/self/auxv")
qt.Assert(t, err, qt.IsNil)
defer av.Close()

_, err = vdsoMemoryAddress(av, binary.LittleEndian)
qt.Assert(t, err, qt.IsNil)
}

func TestLinuxVersionCodeEmbedded(t *testing.T) {
tests := []struct {
file string
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit c41af06

Please sign in to comment.