Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf namespaces: Add reference count checking

Add reference count checking controlled by REFCNT_CHECKING ifdef. The
reference count checking interposes an allocated pointer between the
reference counted struct on a get and frees the pointer on a put.
Accesses after a put cause faults and use after free, missed puts are
caughts as leaks and double puts are double frees.

This checking helped resolve a memory leak and use after free:
https://lore.kernel.org/linux-perf-users/CAP-5=fWZH20L4kv-BwVtGLwR=Em3AOOT+Q4QGivvQuYn5AsPRg@mail.gmail.com/

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexey Bayduraev <alexey.v.bayduraev@linux.intel.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Riccardo Mancini <rickyman7@gmail.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Link: https://lore.kernel.org/lkml/20230407230405.2931830-4-irogers@google.com
[ Extracted from a larger patch ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
c35ce1d9 7031edac

+78 -57
+76 -56
tools/perf/util/namespaces.c
··· 60 60 free(namespaces); 61 61 } 62 62 63 - static int nsinfo__get_nspid(struct nsinfo *nsi, const char *path) 63 + static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const char *path) 64 64 { 65 65 FILE *f = NULL; 66 66 char *statln = NULL; ··· 74 74 while (getline(&statln, &linesz, f) != -1) { 75 75 /* Use tgid if CONFIG_PID_NS is not defined. */ 76 76 if (strstr(statln, "Tgid:") != NULL) { 77 - nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'), 78 - NULL, 10); 79 - nsi->nstgid = nsinfo__tgid(nsi); 77 + *tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10); 78 + *nstgid = *tgid; 80 79 } 81 80 82 81 if (strstr(statln, "NStgid:") != NULL) { 83 82 nspid = strrchr(statln, '\t'); 84 - nsi->nstgid = (pid_t)strtol(nspid, NULL, 10); 83 + *nstgid = (pid_t)strtol(nspid, NULL, 10); 85 84 /* 86 85 * If innermost tgid is not the first, process is in a different 87 86 * PID namespace. 88 87 */ 89 - nsi->in_pidns = (statln + sizeof("NStgid:") - 1) != nspid; 88 + *in_pidns = (statln + sizeof("NStgid:") - 1) != nspid; 90 89 break; 91 90 } 92 91 } ··· 120 121 * want to switch as part of looking up dso/map data. 121 122 */ 122 123 if (old_stat.st_ino != new_stat.st_ino) { 123 - nsi->need_setns = true; 124 - nsi->mntns_path = newns; 124 + RC_CHK_ACCESS(nsi)->need_setns = true; 125 + RC_CHK_ACCESS(nsi)->mntns_path = newns; 125 126 newns = NULL; 126 127 } 127 128 ··· 131 132 if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX) 132 133 goto out; 133 134 134 - rv = nsinfo__get_nspid(nsi, spath); 135 + rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid, 136 + &RC_CHK_ACCESS(nsi)->in_pidns, spath); 135 137 136 138 out: 137 139 free(newns); 138 140 return rv; 141 + } 142 + 143 + static struct nsinfo *nsinfo__alloc(void) 144 + { 145 + struct nsinfo *res; 146 + RC_STRUCT(nsinfo) *nsi; 147 + 148 + nsi = calloc(1, sizeof(*nsi)); 149 + if (ADD_RC_CHK(res, nsi)) 150 + refcount_set(&nsi->refcnt, 1); 151 + 152 + return res; 139 153 } 140 154 141 155 struct nsinfo *nsinfo__new(pid_t pid) ··· 158 146 if (pid == 0) 159 147 return NULL; 160 148 161 - nsi = calloc(1, sizeof(*nsi)); 162 - if (nsi != NULL) { 163 - nsi->pid = pid; 164 - nsi->tgid = pid; 165 - nsi->nstgid = pid; 166 - nsi->need_setns = false; 167 - nsi->in_pidns = false; 168 - /* Init may fail if the process exits while we're trying to look 169 - * at its proc information. In that case, save the pid but 170 - * don't try to enter the namespace. 171 - */ 172 - if (nsinfo__init(nsi) == -1) 173 - nsi->need_setns = false; 149 + nsi = nsinfo__alloc(); 150 + if (!nsi) 151 + return NULL; 174 152 175 - refcount_set(&nsi->refcnt, 1); 176 - } 153 + RC_CHK_ACCESS(nsi)->pid = pid; 154 + RC_CHK_ACCESS(nsi)->tgid = pid; 155 + RC_CHK_ACCESS(nsi)->nstgid = pid; 156 + RC_CHK_ACCESS(nsi)->need_setns = false; 157 + RC_CHK_ACCESS(nsi)->in_pidns = false; 158 + /* Init may fail if the process exits while we're trying to look at its 159 + * proc information. In that case, save the pid but don't try to enter 160 + * the namespace. 161 + */ 162 + if (nsinfo__init(nsi) == -1) 163 + RC_CHK_ACCESS(nsi)->need_setns = false; 177 164 178 165 return nsi; 179 166 } ··· 184 173 if (nsi == NULL) 185 174 return NULL; 186 175 187 - nnsi = calloc(1, sizeof(*nnsi)); 188 - if (nnsi != NULL) { 189 - nnsi->pid = nsinfo__pid(nsi); 190 - nnsi->tgid = nsinfo__tgid(nsi); 191 - nnsi->nstgid = nsinfo__nstgid(nsi); 192 - nnsi->need_setns = nsinfo__need_setns(nsi); 193 - nnsi->in_pidns = nsinfo__in_pidns(nsi); 194 - if (nsi->mntns_path) { 195 - nnsi->mntns_path = strdup(nsi->mntns_path); 196 - if (!nnsi->mntns_path) { 197 - free(nnsi); 198 - return NULL; 199 - } 176 + nnsi = nsinfo__alloc(); 177 + if (!nnsi) 178 + return NULL; 179 + 180 + RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi); 181 + RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi); 182 + RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi); 183 + RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi); 184 + RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi); 185 + if (RC_CHK_ACCESS(nsi)->mntns_path) { 186 + RC_CHK_ACCESS(nnsi)->mntns_path = strdup(RC_CHK_ACCESS(nsi)->mntns_path); 187 + if (!RC_CHK_ACCESS(nnsi)->mntns_path) { 188 + nsinfo__put(nnsi); 189 + return NULL; 200 190 } 201 - refcount_set(&nnsi->refcnt, 1); 202 191 } 203 192 204 193 return nnsi; ··· 206 195 207 196 static void nsinfo__delete(struct nsinfo *nsi) 208 197 { 209 - zfree(&nsi->mntns_path); 210 - free(nsi); 198 + if (nsi) { 199 + WARN_ONCE(refcount_read(&RC_CHK_ACCESS(nsi)->refcnt) != 0, 200 + "nsinfo refcnt unbalanced\n"); 201 + zfree(&RC_CHK_ACCESS(nsi)->mntns_path); 202 + RC_CHK_FREE(nsi); 203 + } 211 204 } 212 205 213 206 struct nsinfo *nsinfo__get(struct nsinfo *nsi) 214 207 { 215 - if (nsi) 216 - refcount_inc(&nsi->refcnt); 217 - return nsi; 208 + struct nsinfo *result; 209 + 210 + if (RC_CHK_GET(result, nsi)) 211 + refcount_inc(&RC_CHK_ACCESS(nsi)->refcnt); 212 + 213 + return result; 218 214 } 219 215 220 216 void nsinfo__put(struct nsinfo *nsi) 221 217 { 222 - if (nsi && refcount_dec_and_test(&nsi->refcnt)) 218 + if (nsi && refcount_dec_and_test(&RC_CHK_ACCESS(nsi)->refcnt)) 223 219 nsinfo__delete(nsi); 220 + else 221 + RC_CHK_PUT(nsi); 224 222 } 225 223 226 224 bool nsinfo__need_setns(const struct nsinfo *nsi) 227 225 { 228 - return nsi->need_setns; 226 + return RC_CHK_ACCESS(nsi)->need_setns; 229 227 } 230 228 231 229 void nsinfo__clear_need_setns(struct nsinfo *nsi) 232 230 { 233 - nsi->need_setns = false; 231 + RC_CHK_ACCESS(nsi)->need_setns = false; 234 232 } 235 233 236 234 pid_t nsinfo__tgid(const struct nsinfo *nsi) 237 235 { 238 - return nsi->tgid; 236 + return RC_CHK_ACCESS(nsi)->tgid; 239 237 } 240 238 241 239 pid_t nsinfo__nstgid(const struct nsinfo *nsi) 242 240 { 243 - return nsi->nstgid; 241 + return RC_CHK_ACCESS(nsi)->nstgid; 244 242 } 245 243 246 244 pid_t nsinfo__pid(const struct nsinfo *nsi) 247 245 { 248 - return nsi->pid; 246 + return RC_CHK_ACCESS(nsi)->pid; 249 247 } 250 248 251 249 pid_t nsinfo__in_pidns(const struct nsinfo *nsi) 252 250 { 253 - return nsi->in_pidns; 251 + return RC_CHK_ACCESS(nsi)->in_pidns; 254 252 } 255 253 256 254 void nsinfo__mountns_enter(struct nsinfo *nsi, ··· 276 256 nc->oldns = -1; 277 257 nc->newns = -1; 278 258 279 - if (!nsi || !nsi->need_setns) 259 + if (!nsi || !RC_CHK_ACCESS(nsi)->need_setns) 280 260 return; 281 261 282 262 if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) ··· 290 270 if (oldns < 0) 291 271 goto errout; 292 272 293 - newns = open(nsi->mntns_path, O_RDONLY); 273 + newns = open(RC_CHK_ACCESS(nsi)->mntns_path, O_RDONLY); 294 274 if (newns < 0) 295 275 goto errout; 296 276 ··· 359 339 360 340 bool nsinfo__is_in_root_namespace(void) 361 341 { 362 - struct nsinfo nsi; 342 + pid_t tgid = 0, nstgid = 0; 343 + bool in_pidns = false; 363 344 364 - memset(&nsi, 0x0, sizeof(nsi)); 365 - nsinfo__get_nspid(&nsi, "/proc/self/status"); 366 - return !nsi.in_pidns; 345 + nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, "/proc/self/status"); 346 + return !in_pidns; 367 347 }
+2 -1
tools/perf/util/namespaces.h
··· 13 13 #include <linux/perf_event.h> 14 14 #include <linux/refcount.h> 15 15 #include <linux/types.h> 16 + #include <internal/rc_check.h> 16 17 17 18 #ifndef HAVE_SETNS_SUPPORT 18 19 int setns(int fd, int nstype); ··· 30 29 struct namespaces *namespaces__new(struct perf_record_namespaces *event); 31 30 void namespaces__free(struct namespaces *namespaces); 32 31 33 - struct nsinfo { 32 + DECLARE_RC_STRUCT(nsinfo) { 34 33 pid_t pid; 35 34 pid_t tgid; 36 35 pid_t nstgid;