Commit 5883f57ca0008ffc93e09cbb9847a1928e50c6f3
Committed by
Linus Torvalds
1 parent
312ec7e50c
Exists in
master
and in
7 other branches
proc: protect mm start_code/end_code in /proc/pid/stat
While mm->start_stack was protected from cross-uid viewing (commit f83ce3e6b02d5 ("proc: avoid information leaks to non-privileged processes")), the start_code and end_code values were not. This would allow the text location of a PIE binary to leak, defeating ASLR. Note that the value "1" is used instead of "0" for a protected value since "ps", "killall", and likely other readers of /proc/pid/stat, take start_code of "0" to mean a kernel thread and will misbehave. Thanks to Brad Spengler for pointing this out. Addresses CVE-2011-0726 Signed-off-by: Kees Cook <kees.cook@canonical.com> Cc: <stable@kernel.org> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: David Howells <dhowells@redhat.com> Cc: Eugene Teo <eugeneteo@kernel.sg> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: Brad Spengler <spender@grsecurity.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 2 additions and 2 deletions Inline Diff
fs/proc/array.c
1 | /* | 1 | /* |
2 | * linux/fs/proc/array.c | 2 | * linux/fs/proc/array.c |
3 | * | 3 | * |
4 | * Copyright (C) 1992 by Linus Torvalds | 4 | * Copyright (C) 1992 by Linus Torvalds |
5 | * based on ideas by Darren Senn | 5 | * based on ideas by Darren Senn |
6 | * | 6 | * |
7 | * Fixes: | 7 | * Fixes: |
8 | * Michael. K. Johnson: stat,statm extensions. | 8 | * Michael. K. Johnson: stat,statm extensions. |
9 | * <johnsonm@stolaf.edu> | 9 | * <johnsonm@stolaf.edu> |
10 | * | 10 | * |
11 | * Pauline Middelink : Made cmdline,envline only break at '\0's, to | 11 | * Pauline Middelink : Made cmdline,envline only break at '\0's, to |
12 | * make sure SET_PROCTITLE works. Also removed | 12 | * make sure SET_PROCTITLE works. Also removed |
13 | * bad '!' which forced address recalculation for | 13 | * bad '!' which forced address recalculation for |
14 | * EVERY character on the current page. | 14 | * EVERY character on the current page. |
15 | * <middelin@polyware.iaf.nl> | 15 | * <middelin@polyware.iaf.nl> |
16 | * | 16 | * |
17 | * Danny ter Haar : added cpuinfo | 17 | * Danny ter Haar : added cpuinfo |
18 | * <dth@cistron.nl> | 18 | * <dth@cistron.nl> |
19 | * | 19 | * |
20 | * Alessandro Rubini : profile extension. | 20 | * Alessandro Rubini : profile extension. |
21 | * <rubini@ipvvis.unipv.it> | 21 | * <rubini@ipvvis.unipv.it> |
22 | * | 22 | * |
23 | * Jeff Tranter : added BogoMips field to cpuinfo | 23 | * Jeff Tranter : added BogoMips field to cpuinfo |
24 | * <Jeff_Tranter@Mitel.COM> | 24 | * <Jeff_Tranter@Mitel.COM> |
25 | * | 25 | * |
26 | * Bruno Haible : remove 4K limit for the maps file | 26 | * Bruno Haible : remove 4K limit for the maps file |
27 | * <haible@ma2s2.mathematik.uni-karlsruhe.de> | 27 | * <haible@ma2s2.mathematik.uni-karlsruhe.de> |
28 | * | 28 | * |
29 | * Yves Arrouye : remove removal of trailing spaces in get_array. | 29 | * Yves Arrouye : remove removal of trailing spaces in get_array. |
30 | * <Yves.Arrouye@marin.fdn.fr> | 30 | * <Yves.Arrouye@marin.fdn.fr> |
31 | * | 31 | * |
32 | * Jerome Forissier : added per-CPU time information to /proc/stat | 32 | * Jerome Forissier : added per-CPU time information to /proc/stat |
33 | * and /proc/<pid>/cpu extension | 33 | * and /proc/<pid>/cpu extension |
34 | * <forissier@isia.cma.fr> | 34 | * <forissier@isia.cma.fr> |
35 | * - Incorporation and non-SMP safe operation | 35 | * - Incorporation and non-SMP safe operation |
36 | * of forissier patch in 2.1.78 by | 36 | * of forissier patch in 2.1.78 by |
37 | * Hans Marcus <crowbar@concepts.nl> | 37 | * Hans Marcus <crowbar@concepts.nl> |
38 | * | 38 | * |
39 | * aeb@cwi.nl : /proc/partitions | 39 | * aeb@cwi.nl : /proc/partitions |
40 | * | 40 | * |
41 | * | 41 | * |
42 | * Alan Cox : security fixes. | 42 | * Alan Cox : security fixes. |
43 | * <alan@lxorguk.ukuu.org.uk> | 43 | * <alan@lxorguk.ukuu.org.uk> |
44 | * | 44 | * |
45 | * Al Viro : safe handling of mm_struct | 45 | * Al Viro : safe handling of mm_struct |
46 | * | 46 | * |
47 | * Gerhard Wichert : added BIGMEM support | 47 | * Gerhard Wichert : added BIGMEM support |
48 | * Siemens AG <Gerhard.Wichert@pdb.siemens.de> | 48 | * Siemens AG <Gerhard.Wichert@pdb.siemens.de> |
49 | * | 49 | * |
50 | * Al Viro & Jeff Garzik : moved most of the thing into base.c and | 50 | * Al Viro & Jeff Garzik : moved most of the thing into base.c and |
51 | * : proc_misc.c. The rest may eventually go into | 51 | * : proc_misc.c. The rest may eventually go into |
52 | * : base.c too. | 52 | * : base.c too. |
53 | */ | 53 | */ |
54 | 54 | ||
55 | #include <linux/types.h> | 55 | #include <linux/types.h> |
56 | #include <linux/errno.h> | 56 | #include <linux/errno.h> |
57 | #include <linux/time.h> | 57 | #include <linux/time.h> |
58 | #include <linux/kernel.h> | 58 | #include <linux/kernel.h> |
59 | #include <linux/kernel_stat.h> | 59 | #include <linux/kernel_stat.h> |
60 | #include <linux/tty.h> | 60 | #include <linux/tty.h> |
61 | #include <linux/string.h> | 61 | #include <linux/string.h> |
62 | #include <linux/mman.h> | 62 | #include <linux/mman.h> |
63 | #include <linux/proc_fs.h> | 63 | #include <linux/proc_fs.h> |
64 | #include <linux/ioport.h> | 64 | #include <linux/ioport.h> |
65 | #include <linux/uaccess.h> | 65 | #include <linux/uaccess.h> |
66 | #include <linux/io.h> | 66 | #include <linux/io.h> |
67 | #include <linux/mm.h> | 67 | #include <linux/mm.h> |
68 | #include <linux/hugetlb.h> | 68 | #include <linux/hugetlb.h> |
69 | #include <linux/pagemap.h> | 69 | #include <linux/pagemap.h> |
70 | #include <linux/swap.h> | 70 | #include <linux/swap.h> |
71 | #include <linux/smp.h> | 71 | #include <linux/smp.h> |
72 | #include <linux/signal.h> | 72 | #include <linux/signal.h> |
73 | #include <linux/highmem.h> | 73 | #include <linux/highmem.h> |
74 | #include <linux/file.h> | 74 | #include <linux/file.h> |
75 | #include <linux/fdtable.h> | 75 | #include <linux/fdtable.h> |
76 | #include <linux/times.h> | 76 | #include <linux/times.h> |
77 | #include <linux/cpuset.h> | 77 | #include <linux/cpuset.h> |
78 | #include <linux/rcupdate.h> | 78 | #include <linux/rcupdate.h> |
79 | #include <linux/delayacct.h> | 79 | #include <linux/delayacct.h> |
80 | #include <linux/seq_file.h> | 80 | #include <linux/seq_file.h> |
81 | #include <linux/pid_namespace.h> | 81 | #include <linux/pid_namespace.h> |
82 | #include <linux/ptrace.h> | 82 | #include <linux/ptrace.h> |
83 | #include <linux/tracehook.h> | 83 | #include <linux/tracehook.h> |
84 | 84 | ||
85 | #include <asm/pgtable.h> | 85 | #include <asm/pgtable.h> |
86 | #include <asm/processor.h> | 86 | #include <asm/processor.h> |
87 | #include "internal.h" | 87 | #include "internal.h" |
88 | 88 | ||
89 | static inline void task_name(struct seq_file *m, struct task_struct *p) | 89 | static inline void task_name(struct seq_file *m, struct task_struct *p) |
90 | { | 90 | { |
91 | int i; | 91 | int i; |
92 | char *buf, *end; | 92 | char *buf, *end; |
93 | char *name; | 93 | char *name; |
94 | char tcomm[sizeof(p->comm)]; | 94 | char tcomm[sizeof(p->comm)]; |
95 | 95 | ||
96 | get_task_comm(tcomm, p); | 96 | get_task_comm(tcomm, p); |
97 | 97 | ||
98 | seq_puts(m, "Name:\t"); | 98 | seq_puts(m, "Name:\t"); |
99 | end = m->buf + m->size; | 99 | end = m->buf + m->size; |
100 | buf = m->buf + m->count; | 100 | buf = m->buf + m->count; |
101 | name = tcomm; | 101 | name = tcomm; |
102 | i = sizeof(tcomm); | 102 | i = sizeof(tcomm); |
103 | while (i && (buf < end)) { | 103 | while (i && (buf < end)) { |
104 | unsigned char c = *name; | 104 | unsigned char c = *name; |
105 | name++; | 105 | name++; |
106 | i--; | 106 | i--; |
107 | *buf = c; | 107 | *buf = c; |
108 | if (!c) | 108 | if (!c) |
109 | break; | 109 | break; |
110 | if (c == '\\') { | 110 | if (c == '\\') { |
111 | buf++; | 111 | buf++; |
112 | if (buf < end) | 112 | if (buf < end) |
113 | *buf++ = c; | 113 | *buf++ = c; |
114 | continue; | 114 | continue; |
115 | } | 115 | } |
116 | if (c == '\n') { | 116 | if (c == '\n') { |
117 | *buf++ = '\\'; | 117 | *buf++ = '\\'; |
118 | if (buf < end) | 118 | if (buf < end) |
119 | *buf++ = 'n'; | 119 | *buf++ = 'n'; |
120 | continue; | 120 | continue; |
121 | } | 121 | } |
122 | buf++; | 122 | buf++; |
123 | } | 123 | } |
124 | m->count = buf - m->buf; | 124 | m->count = buf - m->buf; |
125 | seq_putc(m, '\n'); | 125 | seq_putc(m, '\n'); |
126 | } | 126 | } |
127 | 127 | ||
128 | /* | 128 | /* |
129 | * The task state array is a strange "bitmap" of | 129 | * The task state array is a strange "bitmap" of |
130 | * reasons to sleep. Thus "running" is zero, and | 130 | * reasons to sleep. Thus "running" is zero, and |
131 | * you can test for combinations of others with | 131 | * you can test for combinations of others with |
132 | * simple bit tests. | 132 | * simple bit tests. |
133 | */ | 133 | */ |
134 | static const char *task_state_array[] = { | 134 | static const char *task_state_array[] = { |
135 | "R (running)", /* 0 */ | 135 | "R (running)", /* 0 */ |
136 | "S (sleeping)", /* 1 */ | 136 | "S (sleeping)", /* 1 */ |
137 | "D (disk sleep)", /* 2 */ | 137 | "D (disk sleep)", /* 2 */ |
138 | "T (stopped)", /* 4 */ | 138 | "T (stopped)", /* 4 */ |
139 | "t (tracing stop)", /* 8 */ | 139 | "t (tracing stop)", /* 8 */ |
140 | "Z (zombie)", /* 16 */ | 140 | "Z (zombie)", /* 16 */ |
141 | "X (dead)", /* 32 */ | 141 | "X (dead)", /* 32 */ |
142 | "x (dead)", /* 64 */ | 142 | "x (dead)", /* 64 */ |
143 | "K (wakekill)", /* 128 */ | 143 | "K (wakekill)", /* 128 */ |
144 | "W (waking)", /* 256 */ | 144 | "W (waking)", /* 256 */ |
145 | }; | 145 | }; |
146 | 146 | ||
147 | static inline const char *get_task_state(struct task_struct *tsk) | 147 | static inline const char *get_task_state(struct task_struct *tsk) |
148 | { | 148 | { |
149 | unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state; | 149 | unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state; |
150 | const char **p = &task_state_array[0]; | 150 | const char **p = &task_state_array[0]; |
151 | 151 | ||
152 | BUILD_BUG_ON(1 + ilog2(TASK_STATE_MAX) != ARRAY_SIZE(task_state_array)); | 152 | BUILD_BUG_ON(1 + ilog2(TASK_STATE_MAX) != ARRAY_SIZE(task_state_array)); |
153 | 153 | ||
154 | while (state) { | 154 | while (state) { |
155 | p++; | 155 | p++; |
156 | state >>= 1; | 156 | state >>= 1; |
157 | } | 157 | } |
158 | return *p; | 158 | return *p; |
159 | } | 159 | } |
160 | 160 | ||
161 | static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | 161 | static inline void task_state(struct seq_file *m, struct pid_namespace *ns, |
162 | struct pid *pid, struct task_struct *p) | 162 | struct pid *pid, struct task_struct *p) |
163 | { | 163 | { |
164 | struct group_info *group_info; | 164 | struct group_info *group_info; |
165 | int g; | 165 | int g; |
166 | struct fdtable *fdt = NULL; | 166 | struct fdtable *fdt = NULL; |
167 | const struct cred *cred; | 167 | const struct cred *cred; |
168 | pid_t ppid, tpid; | 168 | pid_t ppid, tpid; |
169 | 169 | ||
170 | rcu_read_lock(); | 170 | rcu_read_lock(); |
171 | ppid = pid_alive(p) ? | 171 | ppid = pid_alive(p) ? |
172 | task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; | 172 | task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; |
173 | tpid = 0; | 173 | tpid = 0; |
174 | if (pid_alive(p)) { | 174 | if (pid_alive(p)) { |
175 | struct task_struct *tracer = tracehook_tracer_task(p); | 175 | struct task_struct *tracer = tracehook_tracer_task(p); |
176 | if (tracer) | 176 | if (tracer) |
177 | tpid = task_pid_nr_ns(tracer, ns); | 177 | tpid = task_pid_nr_ns(tracer, ns); |
178 | } | 178 | } |
179 | cred = get_task_cred(p); | 179 | cred = get_task_cred(p); |
180 | seq_printf(m, | 180 | seq_printf(m, |
181 | "State:\t%s\n" | 181 | "State:\t%s\n" |
182 | "Tgid:\t%d\n" | 182 | "Tgid:\t%d\n" |
183 | "Pid:\t%d\n" | 183 | "Pid:\t%d\n" |
184 | "PPid:\t%d\n" | 184 | "PPid:\t%d\n" |
185 | "TracerPid:\t%d\n" | 185 | "TracerPid:\t%d\n" |
186 | "Uid:\t%d\t%d\t%d\t%d\n" | 186 | "Uid:\t%d\t%d\t%d\t%d\n" |
187 | "Gid:\t%d\t%d\t%d\t%d\n", | 187 | "Gid:\t%d\t%d\t%d\t%d\n", |
188 | get_task_state(p), | 188 | get_task_state(p), |
189 | task_tgid_nr_ns(p, ns), | 189 | task_tgid_nr_ns(p, ns), |
190 | pid_nr_ns(pid, ns), | 190 | pid_nr_ns(pid, ns), |
191 | ppid, tpid, | 191 | ppid, tpid, |
192 | cred->uid, cred->euid, cred->suid, cred->fsuid, | 192 | cred->uid, cred->euid, cred->suid, cred->fsuid, |
193 | cred->gid, cred->egid, cred->sgid, cred->fsgid); | 193 | cred->gid, cred->egid, cred->sgid, cred->fsgid); |
194 | 194 | ||
195 | task_lock(p); | 195 | task_lock(p); |
196 | if (p->files) | 196 | if (p->files) |
197 | fdt = files_fdtable(p->files); | 197 | fdt = files_fdtable(p->files); |
198 | seq_printf(m, | 198 | seq_printf(m, |
199 | "FDSize:\t%d\n" | 199 | "FDSize:\t%d\n" |
200 | "Groups:\t", | 200 | "Groups:\t", |
201 | fdt ? fdt->max_fds : 0); | 201 | fdt ? fdt->max_fds : 0); |
202 | rcu_read_unlock(); | 202 | rcu_read_unlock(); |
203 | 203 | ||
204 | group_info = cred->group_info; | 204 | group_info = cred->group_info; |
205 | task_unlock(p); | 205 | task_unlock(p); |
206 | 206 | ||
207 | for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++) | 207 | for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++) |
208 | seq_printf(m, "%d ", GROUP_AT(group_info, g)); | 208 | seq_printf(m, "%d ", GROUP_AT(group_info, g)); |
209 | put_cred(cred); | 209 | put_cred(cred); |
210 | 210 | ||
211 | seq_putc(m, '\n'); | 211 | seq_putc(m, '\n'); |
212 | } | 212 | } |
213 | 213 | ||
214 | static void render_sigset_t(struct seq_file *m, const char *header, | 214 | static void render_sigset_t(struct seq_file *m, const char *header, |
215 | sigset_t *set) | 215 | sigset_t *set) |
216 | { | 216 | { |
217 | int i; | 217 | int i; |
218 | 218 | ||
219 | seq_puts(m, header); | 219 | seq_puts(m, header); |
220 | 220 | ||
221 | i = _NSIG; | 221 | i = _NSIG; |
222 | do { | 222 | do { |
223 | int x = 0; | 223 | int x = 0; |
224 | 224 | ||
225 | i -= 4; | 225 | i -= 4; |
226 | if (sigismember(set, i+1)) x |= 1; | 226 | if (sigismember(set, i+1)) x |= 1; |
227 | if (sigismember(set, i+2)) x |= 2; | 227 | if (sigismember(set, i+2)) x |= 2; |
228 | if (sigismember(set, i+3)) x |= 4; | 228 | if (sigismember(set, i+3)) x |= 4; |
229 | if (sigismember(set, i+4)) x |= 8; | 229 | if (sigismember(set, i+4)) x |= 8; |
230 | seq_printf(m, "%x", x); | 230 | seq_printf(m, "%x", x); |
231 | } while (i >= 4); | 231 | } while (i >= 4); |
232 | 232 | ||
233 | seq_putc(m, '\n'); | 233 | seq_putc(m, '\n'); |
234 | } | 234 | } |
235 | 235 | ||
236 | static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, | 236 | static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, |
237 | sigset_t *catch) | 237 | sigset_t *catch) |
238 | { | 238 | { |
239 | struct k_sigaction *k; | 239 | struct k_sigaction *k; |
240 | int i; | 240 | int i; |
241 | 241 | ||
242 | k = p->sighand->action; | 242 | k = p->sighand->action; |
243 | for (i = 1; i <= _NSIG; ++i, ++k) { | 243 | for (i = 1; i <= _NSIG; ++i, ++k) { |
244 | if (k->sa.sa_handler == SIG_IGN) | 244 | if (k->sa.sa_handler == SIG_IGN) |
245 | sigaddset(ign, i); | 245 | sigaddset(ign, i); |
246 | else if (k->sa.sa_handler != SIG_DFL) | 246 | else if (k->sa.sa_handler != SIG_DFL) |
247 | sigaddset(catch, i); | 247 | sigaddset(catch, i); |
248 | } | 248 | } |
249 | } | 249 | } |
250 | 250 | ||
251 | static inline void task_sig(struct seq_file *m, struct task_struct *p) | 251 | static inline void task_sig(struct seq_file *m, struct task_struct *p) |
252 | { | 252 | { |
253 | unsigned long flags; | 253 | unsigned long flags; |
254 | sigset_t pending, shpending, blocked, ignored, caught; | 254 | sigset_t pending, shpending, blocked, ignored, caught; |
255 | int num_threads = 0; | 255 | int num_threads = 0; |
256 | unsigned long qsize = 0; | 256 | unsigned long qsize = 0; |
257 | unsigned long qlim = 0; | 257 | unsigned long qlim = 0; |
258 | 258 | ||
259 | sigemptyset(&pending); | 259 | sigemptyset(&pending); |
260 | sigemptyset(&shpending); | 260 | sigemptyset(&shpending); |
261 | sigemptyset(&blocked); | 261 | sigemptyset(&blocked); |
262 | sigemptyset(&ignored); | 262 | sigemptyset(&ignored); |
263 | sigemptyset(&caught); | 263 | sigemptyset(&caught); |
264 | 264 | ||
265 | if (lock_task_sighand(p, &flags)) { | 265 | if (lock_task_sighand(p, &flags)) { |
266 | pending = p->pending.signal; | 266 | pending = p->pending.signal; |
267 | shpending = p->signal->shared_pending.signal; | 267 | shpending = p->signal->shared_pending.signal; |
268 | blocked = p->blocked; | 268 | blocked = p->blocked; |
269 | collect_sigign_sigcatch(p, &ignored, &caught); | 269 | collect_sigign_sigcatch(p, &ignored, &caught); |
270 | num_threads = get_nr_threads(p); | 270 | num_threads = get_nr_threads(p); |
271 | rcu_read_lock(); /* FIXME: is this correct? */ | 271 | rcu_read_lock(); /* FIXME: is this correct? */ |
272 | qsize = atomic_read(&__task_cred(p)->user->sigpending); | 272 | qsize = atomic_read(&__task_cred(p)->user->sigpending); |
273 | rcu_read_unlock(); | 273 | rcu_read_unlock(); |
274 | qlim = task_rlimit(p, RLIMIT_SIGPENDING); | 274 | qlim = task_rlimit(p, RLIMIT_SIGPENDING); |
275 | unlock_task_sighand(p, &flags); | 275 | unlock_task_sighand(p, &flags); |
276 | } | 276 | } |
277 | 277 | ||
278 | seq_printf(m, "Threads:\t%d\n", num_threads); | 278 | seq_printf(m, "Threads:\t%d\n", num_threads); |
279 | seq_printf(m, "SigQ:\t%lu/%lu\n", qsize, qlim); | 279 | seq_printf(m, "SigQ:\t%lu/%lu\n", qsize, qlim); |
280 | 280 | ||
281 | /* render them all */ | 281 | /* render them all */ |
282 | render_sigset_t(m, "SigPnd:\t", &pending); | 282 | render_sigset_t(m, "SigPnd:\t", &pending); |
283 | render_sigset_t(m, "ShdPnd:\t", &shpending); | 283 | render_sigset_t(m, "ShdPnd:\t", &shpending); |
284 | render_sigset_t(m, "SigBlk:\t", &blocked); | 284 | render_sigset_t(m, "SigBlk:\t", &blocked); |
285 | render_sigset_t(m, "SigIgn:\t", &ignored); | 285 | render_sigset_t(m, "SigIgn:\t", &ignored); |
286 | render_sigset_t(m, "SigCgt:\t", &caught); | 286 | render_sigset_t(m, "SigCgt:\t", &caught); |
287 | } | 287 | } |
288 | 288 | ||
289 | static void render_cap_t(struct seq_file *m, const char *header, | 289 | static void render_cap_t(struct seq_file *m, const char *header, |
290 | kernel_cap_t *a) | 290 | kernel_cap_t *a) |
291 | { | 291 | { |
292 | unsigned __capi; | 292 | unsigned __capi; |
293 | 293 | ||
294 | seq_puts(m, header); | 294 | seq_puts(m, header); |
295 | CAP_FOR_EACH_U32(__capi) { | 295 | CAP_FOR_EACH_U32(__capi) { |
296 | seq_printf(m, "%08x", | 296 | seq_printf(m, "%08x", |
297 | a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]); | 297 | a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]); |
298 | } | 298 | } |
299 | seq_putc(m, '\n'); | 299 | seq_putc(m, '\n'); |
300 | } | 300 | } |
301 | 301 | ||
302 | static inline void task_cap(struct seq_file *m, struct task_struct *p) | 302 | static inline void task_cap(struct seq_file *m, struct task_struct *p) |
303 | { | 303 | { |
304 | const struct cred *cred; | 304 | const struct cred *cred; |
305 | kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset; | 305 | kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset; |
306 | 306 | ||
307 | rcu_read_lock(); | 307 | rcu_read_lock(); |
308 | cred = __task_cred(p); | 308 | cred = __task_cred(p); |
309 | cap_inheritable = cred->cap_inheritable; | 309 | cap_inheritable = cred->cap_inheritable; |
310 | cap_permitted = cred->cap_permitted; | 310 | cap_permitted = cred->cap_permitted; |
311 | cap_effective = cred->cap_effective; | 311 | cap_effective = cred->cap_effective; |
312 | cap_bset = cred->cap_bset; | 312 | cap_bset = cred->cap_bset; |
313 | rcu_read_unlock(); | 313 | rcu_read_unlock(); |
314 | 314 | ||
315 | render_cap_t(m, "CapInh:\t", &cap_inheritable); | 315 | render_cap_t(m, "CapInh:\t", &cap_inheritable); |
316 | render_cap_t(m, "CapPrm:\t", &cap_permitted); | 316 | render_cap_t(m, "CapPrm:\t", &cap_permitted); |
317 | render_cap_t(m, "CapEff:\t", &cap_effective); | 317 | render_cap_t(m, "CapEff:\t", &cap_effective); |
318 | render_cap_t(m, "CapBnd:\t", &cap_bset); | 318 | render_cap_t(m, "CapBnd:\t", &cap_bset); |
319 | } | 319 | } |
320 | 320 | ||
321 | static inline void task_context_switch_counts(struct seq_file *m, | 321 | static inline void task_context_switch_counts(struct seq_file *m, |
322 | struct task_struct *p) | 322 | struct task_struct *p) |
323 | { | 323 | { |
324 | seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" | 324 | seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" |
325 | "nonvoluntary_ctxt_switches:\t%lu\n", | 325 | "nonvoluntary_ctxt_switches:\t%lu\n", |
326 | p->nvcsw, | 326 | p->nvcsw, |
327 | p->nivcsw); | 327 | p->nivcsw); |
328 | } | 328 | } |
329 | 329 | ||
330 | static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) | 330 | static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) |
331 | { | 331 | { |
332 | seq_puts(m, "Cpus_allowed:\t"); | 332 | seq_puts(m, "Cpus_allowed:\t"); |
333 | seq_cpumask(m, &task->cpus_allowed); | 333 | seq_cpumask(m, &task->cpus_allowed); |
334 | seq_putc(m, '\n'); | 334 | seq_putc(m, '\n'); |
335 | seq_puts(m, "Cpus_allowed_list:\t"); | 335 | seq_puts(m, "Cpus_allowed_list:\t"); |
336 | seq_cpumask_list(m, &task->cpus_allowed); | 336 | seq_cpumask_list(m, &task->cpus_allowed); |
337 | seq_putc(m, '\n'); | 337 | seq_putc(m, '\n'); |
338 | } | 338 | } |
339 | 339 | ||
340 | int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, | 340 | int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, |
341 | struct pid *pid, struct task_struct *task) | 341 | struct pid *pid, struct task_struct *task) |
342 | { | 342 | { |
343 | struct mm_struct *mm = get_task_mm(task); | 343 | struct mm_struct *mm = get_task_mm(task); |
344 | 344 | ||
345 | task_name(m, task); | 345 | task_name(m, task); |
346 | task_state(m, ns, pid, task); | 346 | task_state(m, ns, pid, task); |
347 | 347 | ||
348 | if (mm) { | 348 | if (mm) { |
349 | task_mem(m, mm); | 349 | task_mem(m, mm); |
350 | mmput(mm); | 350 | mmput(mm); |
351 | } | 351 | } |
352 | task_sig(m, task); | 352 | task_sig(m, task); |
353 | task_cap(m, task); | 353 | task_cap(m, task); |
354 | task_cpus_allowed(m, task); | 354 | task_cpus_allowed(m, task); |
355 | cpuset_task_status_allowed(m, task); | 355 | cpuset_task_status_allowed(m, task); |
356 | task_context_switch_counts(m, task); | 356 | task_context_switch_counts(m, task); |
357 | return 0; | 357 | return 0; |
358 | } | 358 | } |
359 | 359 | ||
360 | static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, | 360 | static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, |
361 | struct pid *pid, struct task_struct *task, int whole) | 361 | struct pid *pid, struct task_struct *task, int whole) |
362 | { | 362 | { |
363 | unsigned long vsize, eip, esp, wchan = ~0UL; | 363 | unsigned long vsize, eip, esp, wchan = ~0UL; |
364 | long priority, nice; | 364 | long priority, nice; |
365 | int tty_pgrp = -1, tty_nr = 0; | 365 | int tty_pgrp = -1, tty_nr = 0; |
366 | sigset_t sigign, sigcatch; | 366 | sigset_t sigign, sigcatch; |
367 | char state; | 367 | char state; |
368 | pid_t ppid = 0, pgid = -1, sid = -1; | 368 | pid_t ppid = 0, pgid = -1, sid = -1; |
369 | int num_threads = 0; | 369 | int num_threads = 0; |
370 | int permitted; | 370 | int permitted; |
371 | struct mm_struct *mm; | 371 | struct mm_struct *mm; |
372 | unsigned long long start_time; | 372 | unsigned long long start_time; |
373 | unsigned long cmin_flt = 0, cmaj_flt = 0; | 373 | unsigned long cmin_flt = 0, cmaj_flt = 0; |
374 | unsigned long min_flt = 0, maj_flt = 0; | 374 | unsigned long min_flt = 0, maj_flt = 0; |
375 | cputime_t cutime, cstime, utime, stime; | 375 | cputime_t cutime, cstime, utime, stime; |
376 | cputime_t cgtime, gtime; | 376 | cputime_t cgtime, gtime; |
377 | unsigned long rsslim = 0; | 377 | unsigned long rsslim = 0; |
378 | char tcomm[sizeof(task->comm)]; | 378 | char tcomm[sizeof(task->comm)]; |
379 | unsigned long flags; | 379 | unsigned long flags; |
380 | 380 | ||
381 | state = *get_task_state(task); | 381 | state = *get_task_state(task); |
382 | vsize = eip = esp = 0; | 382 | vsize = eip = esp = 0; |
383 | permitted = ptrace_may_access(task, PTRACE_MODE_READ); | 383 | permitted = ptrace_may_access(task, PTRACE_MODE_READ); |
384 | mm = get_task_mm(task); | 384 | mm = get_task_mm(task); |
385 | if (mm) { | 385 | if (mm) { |
386 | vsize = task_vsize(mm); | 386 | vsize = task_vsize(mm); |
387 | if (permitted) { | 387 | if (permitted) { |
388 | eip = KSTK_EIP(task); | 388 | eip = KSTK_EIP(task); |
389 | esp = KSTK_ESP(task); | 389 | esp = KSTK_ESP(task); |
390 | } | 390 | } |
391 | } | 391 | } |
392 | 392 | ||
393 | get_task_comm(tcomm, task); | 393 | get_task_comm(tcomm, task); |
394 | 394 | ||
395 | sigemptyset(&sigign); | 395 | sigemptyset(&sigign); |
396 | sigemptyset(&sigcatch); | 396 | sigemptyset(&sigcatch); |
397 | cutime = cstime = utime = stime = cputime_zero; | 397 | cutime = cstime = utime = stime = cputime_zero; |
398 | cgtime = gtime = cputime_zero; | 398 | cgtime = gtime = cputime_zero; |
399 | 399 | ||
400 | if (lock_task_sighand(task, &flags)) { | 400 | if (lock_task_sighand(task, &flags)) { |
401 | struct signal_struct *sig = task->signal; | 401 | struct signal_struct *sig = task->signal; |
402 | 402 | ||
403 | if (sig->tty) { | 403 | if (sig->tty) { |
404 | struct pid *pgrp = tty_get_pgrp(sig->tty); | 404 | struct pid *pgrp = tty_get_pgrp(sig->tty); |
405 | tty_pgrp = pid_nr_ns(pgrp, ns); | 405 | tty_pgrp = pid_nr_ns(pgrp, ns); |
406 | put_pid(pgrp); | 406 | put_pid(pgrp); |
407 | tty_nr = new_encode_dev(tty_devnum(sig->tty)); | 407 | tty_nr = new_encode_dev(tty_devnum(sig->tty)); |
408 | } | 408 | } |
409 | 409 | ||
410 | num_threads = get_nr_threads(task); | 410 | num_threads = get_nr_threads(task); |
411 | collect_sigign_sigcatch(task, &sigign, &sigcatch); | 411 | collect_sigign_sigcatch(task, &sigign, &sigcatch); |
412 | 412 | ||
413 | cmin_flt = sig->cmin_flt; | 413 | cmin_flt = sig->cmin_flt; |
414 | cmaj_flt = sig->cmaj_flt; | 414 | cmaj_flt = sig->cmaj_flt; |
415 | cutime = sig->cutime; | 415 | cutime = sig->cutime; |
416 | cstime = sig->cstime; | 416 | cstime = sig->cstime; |
417 | cgtime = sig->cgtime; | 417 | cgtime = sig->cgtime; |
418 | rsslim = ACCESS_ONCE(sig->rlim[RLIMIT_RSS].rlim_cur); | 418 | rsslim = ACCESS_ONCE(sig->rlim[RLIMIT_RSS].rlim_cur); |
419 | 419 | ||
420 | /* add up live thread stats at the group level */ | 420 | /* add up live thread stats at the group level */ |
421 | if (whole) { | 421 | if (whole) { |
422 | struct task_struct *t = task; | 422 | struct task_struct *t = task; |
423 | do { | 423 | do { |
424 | min_flt += t->min_flt; | 424 | min_flt += t->min_flt; |
425 | maj_flt += t->maj_flt; | 425 | maj_flt += t->maj_flt; |
426 | gtime = cputime_add(gtime, t->gtime); | 426 | gtime = cputime_add(gtime, t->gtime); |
427 | t = next_thread(t); | 427 | t = next_thread(t); |
428 | } while (t != task); | 428 | } while (t != task); |
429 | 429 | ||
430 | min_flt += sig->min_flt; | 430 | min_flt += sig->min_flt; |
431 | maj_flt += sig->maj_flt; | 431 | maj_flt += sig->maj_flt; |
432 | thread_group_times(task, &utime, &stime); | 432 | thread_group_times(task, &utime, &stime); |
433 | gtime = cputime_add(gtime, sig->gtime); | 433 | gtime = cputime_add(gtime, sig->gtime); |
434 | } | 434 | } |
435 | 435 | ||
436 | sid = task_session_nr_ns(task, ns); | 436 | sid = task_session_nr_ns(task, ns); |
437 | ppid = task_tgid_nr_ns(task->real_parent, ns); | 437 | ppid = task_tgid_nr_ns(task->real_parent, ns); |
438 | pgid = task_pgrp_nr_ns(task, ns); | 438 | pgid = task_pgrp_nr_ns(task, ns); |
439 | 439 | ||
440 | unlock_task_sighand(task, &flags); | 440 | unlock_task_sighand(task, &flags); |
441 | } | 441 | } |
442 | 442 | ||
443 | if (permitted && (!whole || num_threads < 2)) | 443 | if (permitted && (!whole || num_threads < 2)) |
444 | wchan = get_wchan(task); | 444 | wchan = get_wchan(task); |
445 | if (!whole) { | 445 | if (!whole) { |
446 | min_flt = task->min_flt; | 446 | min_flt = task->min_flt; |
447 | maj_flt = task->maj_flt; | 447 | maj_flt = task->maj_flt; |
448 | task_times(task, &utime, &stime); | 448 | task_times(task, &utime, &stime); |
449 | gtime = task->gtime; | 449 | gtime = task->gtime; |
450 | } | 450 | } |
451 | 451 | ||
452 | /* scale priority and nice values from timeslices to -20..20 */ | 452 | /* scale priority and nice values from timeslices to -20..20 */ |
453 | /* to make it look like a "normal" Unix priority/nice value */ | 453 | /* to make it look like a "normal" Unix priority/nice value */ |
454 | priority = task_prio(task); | 454 | priority = task_prio(task); |
455 | nice = task_nice(task); | 455 | nice = task_nice(task); |
456 | 456 | ||
457 | /* Temporary variable needed for gcc-2.96 */ | 457 | /* Temporary variable needed for gcc-2.96 */ |
458 | /* convert timespec -> nsec*/ | 458 | /* convert timespec -> nsec*/ |
459 | start_time = | 459 | start_time = |
460 | (unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC | 460 | (unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC |
461 | + task->real_start_time.tv_nsec; | 461 | + task->real_start_time.tv_nsec; |
462 | /* convert nsec -> ticks */ | 462 | /* convert nsec -> ticks */ |
463 | start_time = nsec_to_clock_t(start_time); | 463 | start_time = nsec_to_clock_t(start_time); |
464 | 464 | ||
465 | seq_printf(m, "%d (%s) %c %d %d %d %d %d %u %lu \ | 465 | seq_printf(m, "%d (%s) %c %d %d %d %d %d %u %lu \ |
466 | %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ | 466 | %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ |
467 | %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld\n", | 467 | %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld\n", |
468 | pid_nr_ns(pid, ns), | 468 | pid_nr_ns(pid, ns), |
469 | tcomm, | 469 | tcomm, |
470 | state, | 470 | state, |
471 | ppid, | 471 | ppid, |
472 | pgid, | 472 | pgid, |
473 | sid, | 473 | sid, |
474 | tty_nr, | 474 | tty_nr, |
475 | tty_pgrp, | 475 | tty_pgrp, |
476 | task->flags, | 476 | task->flags, |
477 | min_flt, | 477 | min_flt, |
478 | cmin_flt, | 478 | cmin_flt, |
479 | maj_flt, | 479 | maj_flt, |
480 | cmaj_flt, | 480 | cmaj_flt, |
481 | cputime_to_clock_t(utime), | 481 | cputime_to_clock_t(utime), |
482 | cputime_to_clock_t(stime), | 482 | cputime_to_clock_t(stime), |
483 | cputime_to_clock_t(cutime), | 483 | cputime_to_clock_t(cutime), |
484 | cputime_to_clock_t(cstime), | 484 | cputime_to_clock_t(cstime), |
485 | priority, | 485 | priority, |
486 | nice, | 486 | nice, |
487 | num_threads, | 487 | num_threads, |
488 | start_time, | 488 | start_time, |
489 | vsize, | 489 | vsize, |
490 | mm ? get_mm_rss(mm) : 0, | 490 | mm ? get_mm_rss(mm) : 0, |
491 | rsslim, | 491 | rsslim, |
492 | mm ? mm->start_code : 0, | 492 | mm ? (permitted ? mm->start_code : 1) : 0, |
493 | mm ? mm->end_code : 0, | 493 | mm ? (permitted ? mm->end_code : 1) : 0, |
494 | (permitted && mm) ? mm->start_stack : 0, | 494 | (permitted && mm) ? mm->start_stack : 0, |
495 | esp, | 495 | esp, |
496 | eip, | 496 | eip, |
497 | /* The signal information here is obsolete. | 497 | /* The signal information here is obsolete. |
498 | * It must be decimal for Linux 2.0 compatibility. | 498 | * It must be decimal for Linux 2.0 compatibility. |
499 | * Use /proc/#/status for real-time signals. | 499 | * Use /proc/#/status for real-time signals. |
500 | */ | 500 | */ |
501 | task->pending.signal.sig[0] & 0x7fffffffUL, | 501 | task->pending.signal.sig[0] & 0x7fffffffUL, |
502 | task->blocked.sig[0] & 0x7fffffffUL, | 502 | task->blocked.sig[0] & 0x7fffffffUL, |
503 | sigign .sig[0] & 0x7fffffffUL, | 503 | sigign .sig[0] & 0x7fffffffUL, |
504 | sigcatch .sig[0] & 0x7fffffffUL, | 504 | sigcatch .sig[0] & 0x7fffffffUL, |
505 | wchan, | 505 | wchan, |
506 | 0UL, | 506 | 0UL, |
507 | 0UL, | 507 | 0UL, |
508 | task->exit_signal, | 508 | task->exit_signal, |
509 | task_cpu(task), | 509 | task_cpu(task), |
510 | task->rt_priority, | 510 | task->rt_priority, |
511 | task->policy, | 511 | task->policy, |
512 | (unsigned long long)delayacct_blkio_ticks(task), | 512 | (unsigned long long)delayacct_blkio_ticks(task), |
513 | cputime_to_clock_t(gtime), | 513 | cputime_to_clock_t(gtime), |
514 | cputime_to_clock_t(cgtime)); | 514 | cputime_to_clock_t(cgtime)); |
515 | if (mm) | 515 | if (mm) |
516 | mmput(mm); | 516 | mmput(mm); |
517 | return 0; | 517 | return 0; |
518 | } | 518 | } |
519 | 519 | ||
520 | int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns, | 520 | int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns, |
521 | struct pid *pid, struct task_struct *task) | 521 | struct pid *pid, struct task_struct *task) |
522 | { | 522 | { |
523 | return do_task_stat(m, ns, pid, task, 0); | 523 | return do_task_stat(m, ns, pid, task, 0); |
524 | } | 524 | } |
525 | 525 | ||
526 | int proc_tgid_stat(struct seq_file *m, struct pid_namespace *ns, | 526 | int proc_tgid_stat(struct seq_file *m, struct pid_namespace *ns, |
527 | struct pid *pid, struct task_struct *task) | 527 | struct pid *pid, struct task_struct *task) |
528 | { | 528 | { |
529 | return do_task_stat(m, ns, pid, task, 1); | 529 | return do_task_stat(m, ns, pid, task, 1); |
530 | } | 530 | } |
531 | 531 | ||
532 | int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns, | 532 | int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns, |
533 | struct pid *pid, struct task_struct *task) | 533 | struct pid *pid, struct task_struct *task) |
534 | { | 534 | { |
535 | unsigned long size = 0, resident = 0, shared = 0, text = 0, data = 0; | 535 | unsigned long size = 0, resident = 0, shared = 0, text = 0, data = 0; |
536 | struct mm_struct *mm = get_task_mm(task); | 536 | struct mm_struct *mm = get_task_mm(task); |
537 | 537 | ||
538 | if (mm) { | 538 | if (mm) { |
539 | size = task_statm(mm, &shared, &text, &data, &resident); | 539 | size = task_statm(mm, &shared, &text, &data, &resident); |
540 | mmput(mm); | 540 | mmput(mm); |
541 | } | 541 | } |
542 | seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n", | 542 | seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n", |
543 | size, resident, shared, text, data); | 543 | size, resident, shared, text, data); |
544 | 544 | ||
545 | return 0; | 545 | return 0; |
546 | } | 546 | } |
547 | 547 |