issue1435.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2019 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. //go:build linux && cgo
  5. // +build linux,cgo
  6. package cgotest
  7. import (
  8. "fmt"
  9. "os"
  10. "sort"
  11. "strings"
  12. "syscall"
  13. "testing"
  14. )
  15. // #include <stdio.h>
  16. // #include <stdlib.h>
  17. // #include <pthread.h>
  18. // #include <unistd.h>
  19. // #include <sys/types.h>
  20. //
  21. // pthread_t *t = NULL;
  22. // pthread_mutex_t mu;
  23. // int nts = 0;
  24. // int all_done = 0;
  25. //
  26. // static void *aFn(void *vargp) {
  27. // int done = 0;
  28. // while (!done) {
  29. // usleep(100);
  30. // pthread_mutex_lock(&mu);
  31. // done = all_done;
  32. // pthread_mutex_unlock(&mu);
  33. // }
  34. // return NULL;
  35. // }
  36. //
  37. // void trial(int argc) {
  38. // int i;
  39. // nts = argc;
  40. // t = calloc(nts, sizeof(pthread_t));
  41. // pthread_mutex_init(&mu, NULL);
  42. // for (i = 0; i < nts; i++) {
  43. // pthread_create(&t[i], NULL, aFn, NULL);
  44. // }
  45. // }
  46. //
  47. // void cleanup(void) {
  48. // int i;
  49. // pthread_mutex_lock(&mu);
  50. // all_done = 1;
  51. // pthread_mutex_unlock(&mu);
  52. // for (i = 0; i < nts; i++) {
  53. // pthread_join(t[i], NULL);
  54. // }
  55. // pthread_mutex_destroy(&mu);
  56. // free(t);
  57. // }
  58. import "C"
  59. // compareStatus is used to confirm the contents of the thread
  60. // specific status files match expectations.
  61. func compareStatus(filter, expect string) error {
  62. expected := filter + expect
  63. pid := syscall.Getpid()
  64. fs, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
  65. if err != nil {
  66. return fmt.Errorf("unable to find %d tasks: %v", pid, err)
  67. }
  68. expectedProc := fmt.Sprintf("Pid:\t%d", pid)
  69. foundAThread := false
  70. for _, f := range fs {
  71. tf := fmt.Sprintf("/proc/%s/status", f.Name())
  72. d, err := os.ReadFile(tf)
  73. if err != nil {
  74. // There are a surprising number of ways this
  75. // can error out on linux. We've seen all of
  76. // the following, so treat any error here as
  77. // equivalent to the "process is gone":
  78. // os.IsNotExist(err),
  79. // "... : no such process",
  80. // "... : bad file descriptor.
  81. continue
  82. }
  83. lines := strings.Split(string(d), "\n")
  84. for _, line := range lines {
  85. // Different kernel vintages pad differently.
  86. line = strings.TrimSpace(line)
  87. if strings.HasPrefix(line, "Pid:\t") {
  88. // On loaded systems, it is possible
  89. // for a TID to be reused really
  90. // quickly. As such, we need to
  91. // validate that the thread status
  92. // info we just read is a task of the
  93. // same process PID as we are
  94. // currently running, and not a
  95. // recently terminated thread
  96. // resurfaced in a different process.
  97. if line != expectedProc {
  98. break
  99. }
  100. // Fall through in the unlikely case
  101. // that filter at some point is
  102. // "Pid:\t".
  103. }
  104. if strings.HasPrefix(line, filter) {
  105. if line == expected {
  106. foundAThread = true
  107. break
  108. }
  109. if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") {
  110. // https://github.com/golang/go/issues/46145
  111. // Containers don't reliably output this line in sorted order so manually sort and compare that.
  112. a := strings.Split(line[8:], " ")
  113. sort.Strings(a)
  114. got := strings.Join(a, " ")
  115. if got == expected[8:] {
  116. foundAThread = true
  117. break
  118. }
  119. }
  120. return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
  121. }
  122. }
  123. }
  124. if !foundAThread {
  125. return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc)
  126. }
  127. return nil
  128. }
  129. // test1435 test 9 glibc implemented setuid/gid syscall functions are
  130. // mapped. This test is a slightly more expansive test than that of
  131. // src/syscall/syscall_linux_test.go:TestSetuidEtc() insofar as it
  132. // launches concurrent threads from C code via CGo and validates that
  133. // they are subject to the system calls being tested. For the actual
  134. // Go functionality being tested here, the syscall_linux_test version
  135. // is considered authoritative, but non-trivial improvements to that
  136. // should be mirrored here.
  137. func test1435(t *testing.T) {
  138. if syscall.Getuid() != 0 {
  139. t.Skip("skipping root only test")
  140. }
  141. // Launch some threads in C.
  142. const cts = 5
  143. C.trial(cts)
  144. defer C.cleanup()
  145. vs := []struct {
  146. call string
  147. fn func() error
  148. filter, expect string
  149. }{
  150. {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"},
  151. {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
  152. {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"},
  153. {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
  154. {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"},
  155. {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
  156. {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"},
  157. {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""},
  158. {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"},
  159. {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"},
  160. {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"},
  161. {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
  162. {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"},
  163. {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"},
  164. {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
  165. {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"},
  166. {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"},
  167. {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
  168. {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"},
  169. {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"},
  170. {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
  171. }
  172. for i, v := range vs {
  173. if err := v.fn(); err != nil {
  174. t.Errorf("[%d] %q failed: %v", i, v.call, err)
  175. continue
  176. }
  177. if err := compareStatus(v.filter, v.expect); err != nil {
  178. t.Errorf("[%d] %q comparison: %v", i, v.call, err)
  179. }
  180. }
  181. }