From: Olaf Kirch I have been chasing a corruption of current->group_info on PPC during NFS stress tests. The problem seems to be that nfsd is messing with its group_info quite a bit, while some monitoring processes look at /proc//status and do a get_group_info/put_group_info without any locking. This problem can be reproduced on ppc platforms within a few seconds if you generate some NFS load and do a "cat /proc/XXX/status" of an nfsd thread in a tight loop. I therefore think changes to current->group_info, and querying it from a different process, needs to be protected using the task_lock. (akpm: task->group_info here is safe against exit() because the task holds a ref on group_info which is released in __put_task_struct, and the /proc file has a ref on the task_struct). --- 25-akpm/fs/proc/array.c | 11 +++++++---- 25-akpm/kernel/sys.c | 5 +++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff -puN fs/proc/array.c~race-condition-with-current-group_info fs/proc/array.c --- 25/fs/proc/array.c~race-condition-with-current-group_info Fri May 21 16:22:54 2004 +++ 25-akpm/fs/proc/array.c Fri May 21 16:22:54 2004 @@ -149,6 +149,7 @@ static inline const char * get_task_stat static inline char * task_state(struct task_struct *p, char *buffer) { + struct group_info *group_info; int g; read_lock(&tasklist_lock); @@ -174,12 +175,14 @@ static inline char * task_state(struct t "FDSize:\t%d\n" "Groups:\t", p->files ? p->files->max_fds : 0); + + group_info = p->group_info; + get_group_info(group_info); task_unlock(p); - get_group_info(p->group_info); - for (g = 0; g < min(p->group_info->ngroups,NGROUPS_SMALL); g++) - buffer += sprintf(buffer, "%d ", GROUP_AT(p->group_info,g)); - put_group_info(p->group_info); + for (g = 0; g < min(group_info->ngroups,NGROUPS_SMALL); g++) + buffer += sprintf(buffer, "%d ", GROUP_AT(group_info,g)); + put_group_info(group_info); buffer += sprintf(buffer, "\n"); return buffer; diff -puN kernel/sys.c~race-condition-with-current-group_info kernel/sys.c --- 25/kernel/sys.c~race-condition-with-current-group_info Fri May 21 16:22:54 2004 +++ 25-akpm/kernel/sys.c Fri May 21 16:22:54 2004 @@ -1281,8 +1281,12 @@ int set_current_groups(struct group_info groups_sort(group_info); get_group_info(group_info); + + task_lock(current); old_info = current->group_info; current->group_info = group_info; + task_unlock(current); + put_group_info(old_info); return 0; @@ -1302,6 +1306,7 @@ asmlinkage long sys_getgroups(int gidset if (gidsetsize < 0) return -EINVAL; + /* no need to grab task_lock here; it cannot change */ get_group_info(current->group_info); i = current->group_info->ngroups; if (gidsetsize) { _