Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752075Ab1EKAXN (ORCPT ); Tue, 10 May 2011 20:23:13 -0400 Received: from e1.ny.us.ibm.com ([32.97.182.141]:39889 "EHLO e1.ny.us.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750950Ab1EKAXM (ORCPT ); Tue, 10 May 2011 20:23:12 -0400 From: John Stultz To: LKML Cc: John Stultz , "Ted Ts'o" , KOSAKI Motohiro , David Rientjes , Dave Hansen , Andrew Morton , linux-mm@kvack.org Subject: [PATCH 1/3] comm: Introduce comm_lock seqlock to protect task->comm access Date: Tue, 10 May 2011 17:23:04 -0700 Message-Id: <1305073386-4810-2-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.7.3.2.146.gca209 In-Reply-To: <1305073386-4810-1-git-send-email-john.stultz@linaro.org> References: <1305073386-4810-1-git-send-email-john.stultz@linaro.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4259 Lines: 121 The implicit rules for current->comm access being safe without locking are no longer true. Accessing current->comm without holding the task lock may result in null or incomplete strings (however, access won't run off the end of the string). In order to properly fix this, I've introduced a comm_lock seqlock which will protect comm access and modified get_task_comm() and set_task_comm() to use it. Since there are a number of cases where comm access is open-coded safely grabbing the task_lock(), we preserve the task locking in set_task_comm, so those users are also safe. With this patch, users that access current->comm without a lock are still prone to null/incomplete comm strings, but it should be no worse then it is now. The next step is to go through and convert all comm accesses to use get_task_comm(). This is substantial, but can be done bit by bit, reducing the race windows with each patch. CC: Ted Ts'o CC: KOSAKI Motohiro CC: David Rientjes CC: Dave Hansen CC: Andrew Morton CC: linux-mm@kvack.org Signed-off-by: John Stultz --- fs/exec.c | 25 ++++++++++++++++++++----- include/linux/init_task.h | 1 + include/linux/sched.h | 5 ++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 5e62d26..fcd056a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -998,18 +998,32 @@ static void flush_old_files(struct files_struct * files) char *get_task_comm(char *buf, struct task_struct *tsk) { - /* buf must be at least sizeof(tsk->comm) in size */ - task_lock(tsk); - strncpy(buf, tsk->comm, sizeof(tsk->comm)); - task_unlock(tsk); + unsigned long seq; + + do { + seq = read_seqbegin(&tsk->comm_lock); + + strncpy(buf, tsk->comm, sizeof(tsk->comm)); + + } while (read_seqretry(&tsk->comm_lock, seq)); + return buf; } void set_task_comm(struct task_struct *tsk, char *buf) { - task_lock(tsk); + unsigned long flags; /* + * XXX - Even though comm is protected by comm_lock, + * we take the task_lock here to serialize against + * current users that directly access comm. + * Once those users are removed, we can drop the + * task locking & memsetting. + */ + task_lock(tsk); + write_seqlock_irqsave(&tsk->comm_lock, flags); + /* * Threads may access current->comm without holding * the task lock, so write the string carefully. * Readers without a lock may see incomplete new @@ -1018,6 +1032,7 @@ void set_task_comm(struct task_struct *tsk, char *buf) memset(tsk->comm, 0, TASK_COMM_LEN); wmb(); strlcpy(tsk->comm, buf, sizeof(tsk->comm)); + write_sequnlock_irqrestore(&tsk->comm_lock, flags); task_unlock(tsk); perf_event_comm(tsk); } diff --git a/include/linux/init_task.h b/include/linux/init_task.h index caa151f..b4f7584 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -161,6 +161,7 @@ extern struct cred init_cred; .group_leader = &tsk, \ RCU_INIT_POINTER(.real_cred, &init_cred), \ RCU_INIT_POINTER(.cred, &init_cred), \ + .comm_lock = SEQLOCK_UNLOCKED, \ .comm = "swapper", \ .thread = INIT_THREAD, \ .fs = &init_fs, \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 18d63ce..f9324e4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1333,10 +1333,9 @@ struct task_struct { const struct cred __rcu *cred; /* effective (overridable) subjective task * credentials (COW) */ struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */ - + seqlock_t comm_lock; /* protect's comm */ char comm[TASK_COMM_LEN]; /* executable name excluding path - - access with [gs]et_task_comm (which lock - it with task_lock()) + - access with [gs]et_task_comm - initialized normally by setup_new_exec */ /* file system info */ int link_count, total_link_count; -- 1.7.3.2.146.gca209 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/