Skip to content

Extract eBPF disassembly assets for convenience #5802

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

Open
FlorentRevest opened this issue Feb 18, 2025 · 1 comment
Open

Extract eBPF disassembly assets for convenience #5802

FlorentRevest opened this issue Feb 18, 2025 · 1 comment

Comments

@FlorentRevest
Copy link
Contributor

FlorentRevest commented Feb 18, 2025

When looking at a reproducer such as https://syzkaller.appspot.com/text?tag=ReproSyz&x=112d9a08e80000 the bpf$PROG_LOAD syscall is used with a [@ANYBLOB="18000000000000000000000000000000611200000000000095000000000000001383[...]"] argument that contains BPF bytecode.

It would be cool to extract the content of that buffer and pass it through objdump to extract the BPF instructions and upload them as assets to the dashboard, a bit like what we do for mounted file systems and fsck reports already.

This is not super high priority for me so I don't really intend to work on it but I wanted to write the idea down somewhere... :)

Also, here is a small poc that can be slapped into prog/bpf_disas_test.go. This is hacked together but goes as far as my curiosity could lead me:

// Copyright 2024 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package prog_test

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/google/syzkaller/prog"
	"github.com/google/syzkaller/sys/targets"
)

func Disassemble(insn []byte) (string, error) {
	tempFile, err := os.CreateTemp("", "*.bpf")
	if err != nil {
		return "", fmt.Errorf("failed to create temporary file: %w", err)
	}
	defer os.Remove(tempFile.Name())

	if _, err := tempFile.Write(insn); err != nil {
		return "", fmt.Errorf("failed to write bytecode to temporary file: %w", err)
	}

	if err := tempFile.Close(); err != nil {
		return "", fmt.Errorf("failed to close temporary file: %w", err)
	}

	cmd := exec.Command(
		"/usr/lib/bpf/bin/objdump", // Distributed by the binutils-bpf debian package
		"-D",
		"-b", "binary",
		"-m", "bpf",
		tempFile.Name(),
	)

	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("error running llvm-objdump: %w, output: %s", err, output)
	}

	// Strip off the header and last line
	lines := strings.Split(string(output), "\n")
	strippedOutput := ""
	if len(lines) > 7 {
		strippedOutput = strings.Join(lines[7:len(lines)-1], "\n")
	}

	return strippedOutput, nil
}

func TestBpfDisas(t *testing.T) {
	target, err := prog.GetTarget(targets.Linux, targets.AMD64)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		prog  string
		disas string
	}{
		{
			`r0 = bpf$PROG_LOAD(0x5, &(0x7f0000000080)={0x9, 0x4, &(0x7f0000000700)=ANY=[@ANYBLOB="18000000000000000000000000000000611200000000000095000000000000001383096e16281fd43e588cf7a1e65f316e5e5600f1fb642cb352b9d4c50ae8366e5cadf97f4e52fdb37bdab01f9f6cc297b10500c98ea973fbaf38f9d47c5702c2bd9ebf0134b54dbee7458404277462d8ac80053e629d28aa5b25e324fd54d237d7921ff7b52f78ad9692619113594630a9eb6490c61332499f4861a57120ea351e61ca79b452a2bffd133c9ce1b4049b537a6310d0ee13db80ad6553ed19a04679d0d66bf61277501f370105113bd565ae2e766f9a79e314ecbc4000b4702ecfcaed9cb384edf20b1d3e7011bd384577a5a78efdd8687e0574465e490aa62e217fa49e4167d7edcd030c20937155d065ee7bb686bffcf28ec73d58a1d795c358c5aee99cae4c959ba2b9a78b4e231c46f8030523faf5b79ef84c5201a69d776df2041ae3d19a3d03fb1f2913fdd3fef24c94f1e224f872c1bebc0a7622231b2be88508a13a5b74e417cdad2076dd0ccdf44daf7404337f84783856b8582065669a46c1d570cdf4d6ce259d39fdc6bc4f066eb27ba18fc0110ebf3eb081d09b8587c911260c2ca2f49825e10b20733735ec2f4a80308c92dac2cac1608cbd739d385703e2933fda0dde43f3270d7170a7f5ce1dad0a2ae4691cc8487e113b89df89fd1d3c51723d79966e8c2eae12cf2dcfa7c09b15de3f494c5bfc35a8ac8124fb66066b2b3c7db6585b2fe802e86d2794d885c779de4ab1a0999fcedaea0b0497927b536e120212681673509f2aa7ad0875d5be6fa5f5812dd7966978f435924026737b78156906c3faf9e84f0cfb70a8d326262ce7ceadf4f95a7afb2bdf8250af753f32"], &(0x7f0000000100)='GPL\x00'}, 0x70)`,
			`   0:	18 00 00 00 00 00 00 00 	lddw %r0,0
   8:	00 00 00 00 00 00 00 00 
  10:	61 12 00 00 00 00 00 00 	ldxw %r1,[%r2+0]
  18:	95 00 00 00 00 00 00 00 	exit`,
		},
	}
	for i, test := range tests {
		t.Run(fmt.Sprint(i), func(t *testing.T) {
			p, err := target.Deserialize([]byte(test.prog), prog.NonStrict)
			if err != nil {
				t.Fatalf("failed to deserialize prog: %v", err)
			}
			structPtrArg := p.Calls[0].Args[1].(*prog.PointerArg)
			structGrpArg := structPtrArg.Res.(*prog.GroupArg)

			insnCntArg := structGrpArg.Inner[1].(*prog.ConstArg)
			insnCnt := int(insnCntArg.Val)

			insnPtrArg := structGrpArg.Inner[2].(*prog.PointerArg)
			insnGrpArg := insnPtrArg.Res.(*prog.GroupArg)
			insnUnionArg := insnGrpArg.Inner[0].(*prog.UnionArg)
			insnArg := insnUnionArg.Option.(*prog.DataArg)
			insn := insnArg.Data()[:insnCnt*8]

			disas, err := Disassemble(insn)

			if disas != test.disas {
				t.Fatalf("bad disas result:\n%v\nwant:\n%v", disas, test.disas)
			}
		})
	}
}
@dvyukov
Copy link
Collaborator

dvyukov commented Feb 18, 2025

cc @pchaigno

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants