@@ -19,13 +19,15 @@ var (
1919
2020 //go:embed testdata/af_vsock.c
2121 afVSOCKSource string
22+
23+ //go:embed testdata/af_alg_socketcall.c
24+ afALGSocketcallSource string
2225)
2326
2427// compileAndExecSocketDenied writes a C source file into the container,
2528// compiles it with the given compiler command, runs the binary as uid 1000,
26- // and asserts that socket creation fails with a permission or
27- // address-family error (not EFAULT or other unrelated failures).
28- func compileAndExecSocketDenied (ctx context.Context , t * testing.T , apiClient client.APIClient , cID string , name string , src string , cc []string ) {
29+ // and asserts that socket creation fails with the expected error.
30+ func compileAndExecSocketDenied (ctx context.Context , t * testing.T , apiClient client.APIClient , cID string , name string , src string , cc []string , expectedErr string ) {
2931 t .Helper ()
3032
3133 binPath := "/tmp/" + name
@@ -50,12 +52,7 @@ func compileAndExecSocketDenied(ctx context.Context, t *testing.T, apiClient cli
5052
5153 out := strings .ToLower (res .Combined ())
5254 assert .Check (t , is .Contains (out , "socket" ), "expected socket-related error message" )
53-
54- // Seccomp blocks return either EPERM ("operation not permitted") or
55- // EAFNOSUPPORT ("address family not supported"). Make sure the failure
56- // is from seccomp, not from a bogus pointer (EFAULT) or other issue.
57- permErr := strings .Contains (out , "not permitted" ) || strings .Contains (out , "not supported" )
58- assert .Check (t , permErr , "expected EPERM or EAFNOSUPPORT, got: %s" , res .Combined ())
55+ assert .Check (t , is .Contains (out , expectedErr ), "expected %s, got: %s" , expectedErr , res .Combined ())
5956}
6057
6158// TestExecSocketDenied verifies that AF_ALG and AF_VSOCK sockets cannot be
@@ -77,10 +74,39 @@ func TestExecSocketDenied(t *testing.T) {
7774
7875 gcc := []string {"gcc" }
7976
77+ arch := testEnv .DaemonInfo .Architecture
78+ isAmd64 := arch == "amd64" || arch == "x86_64"
79+
8080 t .Run ("AF_ALG" , func (t * testing.T ) {
81- compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_ALG" , afALGSource , gcc )
81+ compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_ALG" , afALGSource , gcc , "not permitted" )
8282 })
8383 t .Run ("AF_VSOCK" , func (t * testing.T ) {
84- compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_VSOCK" , afVSOCKSource , gcc )
84+ compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_VSOCK" , afVSOCKSource , gcc , "not permitted" )
85+ })
86+
87+ // Test AF_ALG via the socketcall(2) multiplexer using int $0x80 to
88+ // invoke the ia32 compat syscall path from a native 64-bit binary.
89+ // MAP_32BIT is used to place the args array below 4 GB, since the
90+ // ia32 compat path truncates all registers to 32 bits.
91+ t .Run ("AF_ALG_socketcall_int80" , func (t * testing.T ) {
92+ skip .If (t , ! isAmd64 , "int $0x80 ia32 compat only available on amd64" )
93+
94+ compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_ALG_socketcall_int80" , afALGSocketcallSource , gcc , "not implemented" )
95+ })
96+
97+ // Test AF_ALG with a real i386 binary cross-compiled from amd64. glibc
98+ // on i386 routes socket() through the socketcall(2) multiplexer, which
99+ // is a different seccomp path than the native socket(2) syscall.
100+ t .Run ("AF_ALG_socketcall_i386" , func (t * testing.T ) {
101+ skip .If (t , ! isAmd64 , "i386 cross-compilation only available on amd64" )
102+
103+ res := container .ExecT (ctx , t , apiClient , cID , []string {
104+ "sh" , "-c" , "apt-get install -y --no-install-recommends gcc-i686-linux-gnu libc6-dev-i386-cross linux-libc-dev-i386-cross" ,
105+ })
106+ res .AssertSuccess (t )
107+
108+ compileAndExecSocketDenied (ctx , t , apiClient , cID , "AF_ALG_socketcall_i386" , afALGSource ,
109+ []string {"i686-linux-gnu-gcc" , "-static" }, "not implemented" ,
110+ )
85111 })
86112}
0 commit comments