github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/exec/lp_linux_test.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package exec_test 6 7 import ( 8 "errors" 9 "internal/syscall/unix" 10 "internal/testenv" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "syscall" 15 "testing" 16 ) 17 18 func TestFindExecutableVsNoexec(t *testing.T) { 19 t.Parallel() 20 21 // This test case relies on faccessat2(2) syscall, which appeared in Linux v5.8. 22 if major, minor := unix.KernelVersion(); major < 5 || (major == 5 && minor < 8) { 23 t.Skip("requires Linux kernel v5.8 with faccessat2(2) syscall") 24 } 25 26 tmp := t.TempDir() 27 28 // Create a tmpfs mount. 29 err := syscall.Mount("tmpfs", tmp, "tmpfs", 0, "") 30 if testenv.SyscallIsNotSupported(err) { 31 // Usually this means lack of CAP_SYS_ADMIN, but there might be 32 // other reasons, especially in restricted test environments. 33 t.Skipf("requires ability to mount tmpfs (%v)", err) 34 } else if err != nil { 35 t.Fatalf("mount %s failed: %v", tmp, err) 36 } 37 t.Cleanup(func() { 38 if err := syscall.Unmount(tmp, 0); err != nil { 39 t.Error(err) 40 } 41 }) 42 43 // Create an executable. 44 path := filepath.Join(tmp, "program") 45 err = os.WriteFile(path, []byte("#!/bin/sh\necho 123\n"), 0o755) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 // Check that it works as expected. 51 _, err = exec.LookPath(path) 52 if err != nil { 53 t.Fatalf("findExecutable: got %v, want nil", err) 54 } 55 56 for { 57 err = exec.Command(path).Run() 58 if err == nil { 59 break 60 } 61 if errors.Is(err, syscall.ETXTBSY) { 62 // A fork+exec in another process may be holding open the FD that we used 63 // to write the executable (see https://go.dev/issue/22315). 64 // Since the descriptor should have CLOEXEC set, the problem should resolve 65 // as soon as the forked child reaches its exec call. 66 // Keep retrying until that happens. 67 } else { 68 t.Fatalf("exec: got %v, want nil", err) 69 } 70 } 71 72 // Remount with noexec flag. 73 err = syscall.Mount("", tmp, "", syscall.MS_REMOUNT|syscall.MS_NOEXEC, "") 74 if testenv.SyscallIsNotSupported(err) { 75 t.Skipf("requires ability to re-mount tmpfs (%v)", err) 76 } else if err != nil { 77 t.Fatalf("remount %s with noexec failed: %v", tmp, err) 78 } 79 80 if err := exec.Command(path).Run(); err == nil { 81 t.Fatal("exec on noexec filesystem: got nil, want error") 82 } 83 84 _, err = exec.LookPath(path) 85 if err == nil { 86 t.Fatalf("LookPath: got nil, want error") 87 } 88 }