1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	
4    	#include "numa.h"
5    	
6    	#include <cstring>
7    	#include <errno.h>
8    	#include <iostream>
9    	
10   	#include "include/stringify.h"
11   	#include "common/safe_io.h"
12   	
13   	
14   	// list
15   	#if defined(__linux__)
16   	int parse_cpu_set_list(const char *s,
17   			       size_t *cpu_set_size,
18   			       cpu_set_t *cpu_set)
19   	{
20   	  CPU_ZERO(cpu_set);
(1) Event cond_true: Condition "*s", taking true branch.
21   	  while (*s) {
22   	    char *end;
(2) Event tainted_data_transitive: Call to function "strtol" with tainted argument "s" returns tainted data.
(3) Event var_assign_alias: Assigning: "a" = "strtol(s, &end, 10)". Both are now tainted.
Also see events: [var_assign_alias][a_loop_bound]
23   	    int a = strtol(s, &end, 10);
(4) Event cond_false: Condition "end == s", taking false branch.
24   	    if (end == s) {
25   	      return -EINVAL;
(5) Event if_end: End of if statement.
26   	    }
(6) Event cond_true: Condition "*end == '-'", taking true branch.
27   	    if (*end == '-') {
28   	      s = end + 1;
(7) Event var_assign_alias: Assigning: "b" = "strtol(s, &end, 10)". Both are now tainted.
Also see events: [tainted_data_transitive][var_assign_alias][a_loop_bound]
29   	      int b = strtol(s, &end, 10);
(8) Event cond_false: Condition "end == s", taking false branch.
30   	      if (end == s) {
31   		return -EINVAL;
(9) Event if_end: End of if statement.
32   	      }
(10) Event a_loop_bound: Using tainted variable "b" as a loop boundary.
Also see events: [tainted_data_transitive][var_assign_alias][var_assign_alias]
33   	      for (; a <= b; ++a) {
34   		CPU_SET(a, cpu_set);
35   	      }
36   	      *cpu_set_size = a;
37   	    } else {
38   	      CPU_SET(a, cpu_set);
39   	      *cpu_set_size = a + 1;
40   	    }
41   	    if (*end == 0) {
42   	      break;
43   	    }
44   	    if (*end != ',') {
45   	      return -EINVAL;
46   	    }
47   	    s = end + 1;
48   	  }
49   	  return 0;
50   	}
51   	
52   	std::string cpu_set_to_str_list(size_t cpu_set_size,
53   					const cpu_set_t *cpu_set)
54   	{
55   	  std::string r;
56   	  unsigned a = 0;
57   	  while (true) {
58   	    while (a < cpu_set_size && !CPU_ISSET(a, cpu_set)) {
59   	      ++a;
60   	    }
61   	    if (a >= cpu_set_size) {
62   	      break;
63   	    }
64   	    unsigned b = a + 1;
65   	    while (b < cpu_set_size && CPU_ISSET(b, cpu_set)) {
66   	      ++b;
67   	    }
68   	    if (r.size()) {
69   	      r += ",";
70   	    }
71   	    if (b > a + 1) {
72   	      r += stringify(a) + "-" + stringify(b - 1);
73   	    } else {
74   	      r += stringify(a);
75   	    }
76   	    a = b;
77   	  }
78   	  return r;
79   	}
80   	
81   	std::set<int> cpu_set_to_set(size_t cpu_set_size,
82   				     const cpu_set_t *cpu_set)
83   	{
84   	  set<int> r;
85   	  unsigned a = 0;
86   	  while (true) {
87   	    while (a < cpu_set_size && !CPU_ISSET(a, cpu_set)) {
88   	      ++a;
89   	    }
90   	    if (a >= cpu_set_size) {
91   	      break;
92   	    }
93   	    unsigned b = a + 1;
94   	    while (b < cpu_set_size && CPU_ISSET(b, cpu_set)) {
95   	      ++b;
96   	    }
97   	    while (a < b) {
98   	      r.insert(a);
99   	      ++a;
100  	    }
101  	  }
102  	  return r;
103  	}
104  	
105  	
106  	int get_numa_node_cpu_set(
107  	  int node,
108  	  size_t *cpu_set_size,
109  	  cpu_set_t *cpu_set)
110  	{
111  	  std::string fn = "/sys/devices/system/node/node";
112  	  fn += stringify(node);
113  	  fn += "/cpulist";
114  	  int fd = ::open(fn.c_str(), O_RDONLY);
(1) Event cond_false: Condition "fd < 0", taking false branch.
115  	  if (fd < 0) {
116  	    return -errno;
(2) Event if_end: End of if statement.
117  	  }
118  	  char buf[1024];
(3) Event tainted_data_argument: Calling function "safe_read" taints argument "buf". [details]
Also see events: [tainted_data]
119  	  int r = safe_read(fd, &buf, sizeof(buf));
(4) Event cond_false: Condition "r < 0", taking false branch.
120  	  if (r < 0) {
121  	    goto out;
(5) Event if_end: End of if statement.
122  	  }
123  	  buf[r] = 0;
(6) Event cond_true: Condition "r > 0", taking true branch.
(7) Event cond_true: Condition "isspace(buf[--r])", taking true branch.
(9) Event loop_begin: Jumped back to beginning of loop.
(10) Event cond_false: Condition "r > 0", taking false branch.
124  	  while (r > 0 && ::isspace(buf[--r])) {
125  	    buf[r] = 0;
(8) Event loop: Jumping back to the beginning of the loop.
(11) Event loop_end: Reached end of loop.
126  	  }
(12) Event tainted_data: Passing tainted variable "buf" to a tainted sink. [details]
Also see events: [tainted_data_argument]
127  	  r = parse_cpu_set_list(buf, cpu_set_size, cpu_set);
128  	  if (r < 0) {
129  	    goto out;
130  	  }
131  	  r = 0;
132  	 out:
133  	  ::close(fd);
134  	  return r;
135  	}
136  	
137  	static int easy_readdir(const std::string& dir, std::set<std::string> *out)
138  	{
139  	  DIR *h = ::opendir(dir.c_str());
140  	  if (!h) {
141  	    return -errno;
142  	  }
143  	  struct dirent *de = nullptr;
144  	  while ((de = ::readdir(h))) {
145  	    if (strcmp(de->d_name, ".") == 0 ||
146  		strcmp(de->d_name, "..") == 0) {
147  	      continue;
148  	    }
149  	    out->insert(de->d_name);
150  	  }
151  	  closedir(h);
152  	  return 0;
153  	}
154  	
155  	int set_cpu_affinity_all_threads(size_t cpu_set_size, cpu_set_t *cpu_set)
156  	{
157  	  // first set my affinity
158  	  int r = sched_setaffinity(getpid(), cpu_set_size, cpu_set);
159  	  if (r < 0) {
160  	    return -errno;
161  	  }
162  	
163  	  // make 2 passes here so that we (hopefully) catch racing threads creating
164  	  // threads.
165  	  for (unsigned pass = 0; pass < 2; ++pass) {
166  	    // enumerate all child threads from /proc
167  	    std::set<std::string> ls;
168  	    std::string path = "/proc/"s + stringify(getpid()) + "/task";
169  	    r = easy_readdir(path, &ls);
170  	    if (r < 0) {
171  	      return r;
172  	    }
173  	    for (auto& i : ls) {
174  	      pid_t tid = atoll(i.c_str());
175  	      if (!tid) {
176  		continue;  // wtf
177  	      }
178  	      r = sched_setaffinity(tid, cpu_set_size, cpu_set);
179  	      if (r < 0) {
180  		return -errno;
181  	      }
182  	    }
183  	  }
184  	  return 0;
185  	}
186  	
187  	#elif defined(__FreeBSD__)
188  	
189  	int parse_cpu_set_list(const char *s,
190  			       size_t *cpu_set_size,
191  			       cpu_set_t *cpu_set)
192  	{
193  	  return -ENOTSUP;
194  	}
195  	
196  	std::string cpu_set_to_str_list(size_t cpu_set_size,
197  					const cpu_set_t *cpu_set)
198  	{
199  	  return {};
200  	}
201  	
202  	std::set<int> cpu_set_to_set(size_t cpu_set_size,
203  				     const cpu_set_t *cpu_set)
204  	{
205  	  return {};
206  	}
207  	
208  	int get_numa_node_cpu_set(int node,
209  	                          size_t *cpu_set_size,
210  	                          cpu_set_t *cpu_set)
211  	{
212  	  return -ENOTSUP;
213  	}
214  	
215  	int set_cpu_affinity_all_threads(size_t cpu_set_size,
216  					 cpu_set_t *cpu_set)
217  	{
218  	  return -ENOTSUP;
219  	}
220  	
221  	#endif
222