Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda committed Jan 27, 2025
1 parent 61b0dc5 commit 0f7cab6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 14 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,13 @@ level=WARN msg=***Blocked*** syscall=pidfd_open module=github.com/AkihiroSuda/go
- No isolation of file descriptors across modules.
A confined module can still read/write an existing file descriptor, although it cannot open a new file descriptor.
- The target binary file must not be replaced during execution.
- May not work with a future version of Go.
- The `gomodjail:confined` policy is not well defined and still subject to change.
- This is not a panacea; there can be other loopholes too.

macOS:
- The protection can be disabled by unsetting an environment variable.
- Only works with the following versions of Go. Modify [`libgomodjail_hook_darwin/offset.h`](./libgomodjail_hook_darwin/offset.h) to support other Go versions.
- 1.23.5
- The protection can be arbitraliry disabled by unsetting an environment variable `DYLD_INSERT_LIBRARIES`.

## Advanced topics
### Advanced usage
Expand Down Expand Up @@ -121,7 +122,9 @@ Linux:
- [Stack unwinding](https://www.grant.pizza/blog/go-stack-traces-bpf/) is used for analyzing the call stack to determine the Go module.

macOS:
- `DYLD_INSERT_LIBRARIES` is used to hook `libSystem` (`libc`) calls
- `DYLD_INSERT_LIBRARIES` is used to hook `libSystem` (`libc`) calls.
- In addition to the frame pointer (AArch64 register X29), `struct g` (pointed by X28) and `g->m.libcallsp` are parsed to analyze the CGO call stack.
This analysis is not robust and only works with a specific version of Go.

### Future works
- Automatically detect non-applicable modules (explained in [Caveats](#caveats)).
Expand Down
35 changes: 24 additions & 11 deletions libgomodjail_hook_darwin/libgomodjail_hook_darwin.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "offset.h"
#include <dlfcn.h>
#include <errno.h>
#include <execinfo.h>
Expand Down Expand Up @@ -42,12 +43,7 @@ static bool debug = true;
#error "Unsupported architecture"
#endif

struct __attribute__((packed)) g {
void *stack_lo;
void *stack_hi;
};

static bool handle_syscall(const char *syscall_name) {
static bool handle_syscall(const char *syscall_name, long args[6]) {
bool res = true;
int sock = -1;
char *json_buf = NULL;
Expand Down Expand Up @@ -92,10 +88,27 @@ static bool handle_syscall(const char *syscall_name) {
fprintf(json_fp, "{\"file\":\"%s\",\"symbol\":\"%s\"},", dli.dli_fname,
dli.dli_sname);
if (strcmp(dli.dli_sname, "runtime.asmcgocall.abi0") == 0) {
struct g *g;
FETCH_G(g);
DEBUGF("g: %p %p", g->stack_lo, g->stack_hi);
/* TODO: switch the call stack */
uint64_t g_addr;
FETCH_G(g_addr);
uint64_t m_addr_addr = g_addr + GOMODJAIL_OFFSET_M_IN_G;
uint64_t m_addr = *(uint64_t *)m_addr_addr;
uint64_t libcallpc_addr = m_addr + GOMODJAIL_OFFSET_LIBCALLPC_IN_M;
uint64_t libcallsp_addr = m_addr + GOMODJAIL_OFFSET_LIBCALLSP_IN_M;
uint64_t pc = *(uint64_t *)libcallpc_addr;
uint64_t sp = *(uint64_t *)libcallsp_addr;
uint64_t bp = sp - 8;
while (bp != 0) {
uint64_t saved_bp = *(uint64_t *)bp;
uint64_t ret_addr = *(uint64_t *)(bp + 8);
Dl_info dli2;
if (dladdr((void *)pc, &dli2) > 0) {
DEBUGF("* %s\t%s", dli2.dli_fname, dli2.dli_sname);
fprintf(json_fp, "{\"file\":\"%s\",\"symbol\":\"%s\"},",
dli2.dli_fname, dli2.dli_sname);
}
pc = ret_addr;
bp = saved_bp;
}
}
} else {
DEBUGF("* %p", callstack[i]);
Expand Down Expand Up @@ -150,7 +163,7 @@ typedef int (*syscall6_fn)(long, long, long, long, long, long);
DEBUGF("%s %s", #fn, (char *)args[0]); \
else \
DEBUGF("%s", #fn); \
if (handle_syscall(#fn)) \
if (handle_syscall(#fn, args)) \
return ((syscall6_fn)fn)(args[0], args[1], args[2], args[3], args[4], \
args[5]); \
errno = EPERM; \
Expand Down
4 changes: 4 additions & 0 deletions libgomodjail_hook_darwin/offset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* go1.23.5 */
#define GOMODJAIL_OFFSET_M_IN_G 48
#define GOMODJAIL_OFFSET_LIBCALLPC_IN_M 832
#define GOMODJAIL_OFFSET_LIBCALLSP_IN_M 840
36 changes: 36 additions & 0 deletions libgomodjail_hook_darwin/offset.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh
set -eux -o pipefail

: "${GOROOT:=$(go env GOROOT)}"

cat <<EOF >"${GOROOT}"/src/runtime/gomodjail_offset.go
package runtime
import "unsafe"
var (
GomodjailOffsetMinG = unsafe.Offsetof(g{}.m)
GomodjailOffsetLibcallpcInM = unsafe.Offsetof(m{}.libcallpc)
GomodjailOffsetLibcallspInM = unsafe.Offsetof(m{}.libcallsp)
)
EOF

cat <<EOF >gomodjail_offset_print.go
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Printf("/* %s */\n", runtime.Version())
fmt.Printf("#define GOMODJAIL_OFFSET_M_IN_G %d\n", runtime.GomodjailOffsetMinG)
fmt.Printf("#define GOMODJAIL_OFFSET_LIBCALLPC_IN_M %d\n", runtime.GomodjailOffsetLibcallpcInM)
fmt.Printf("#define GOMODJAIL_OFFSET_LIBCALLSP_IN_M %d\n", runtime.GomodjailOffsetLibcallspInM)
}
EOF

go run ./gomodjail_offset_print.go

rm -f "${GOROOT}"/src/runtime/gomodjail_offset.go gomodjail_offset_print.go

0 comments on commit 0f7cab6

Please sign in to comment.