diff options
author | Philippe Mikoyan <philippe.mikoyan@skat.systems> | 2018-02-06 18:40:49 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-06 21:32:46 -0500 |
commit | 87ad4b0d853e8a65d6002f4e7bd3dce4ae3a52da (patch) | |
tree | c1e7faec69a13a77606fd6e8c9578080ffe531e1 /ipc | |
parent | bac7a1fff7926fb9891a18fe33650884b0e13e41 (diff) |
ipc: fix ipc data structures inconsistency
As described in the title, this patch fixes <ipc>id_ds inconsistency when
<ipc>ctl_stat executes concurrently with some ds-changing function, e.g.
shmat, msgsnd or whatever.
For instance, if shmctl(IPC_STAT) is running concurrently
with shmat, following data structure can be returned:
{... shm_lpid = 0, shm_nattch = 1, ...}
Link: http://lkml.kernel.org/r/20171202153456.6514-1-philippe.mikoyan@skat.systems
Signed-off-by: Philippe Mikoyan <philippe.mikoyan@skat.systems>
Reviewed-by: Davidlohr Bueso <dbueso@suse.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Manfred Spraul <manfred@colorfullife.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/msg.c | 20 | ||||
-rw-r--r-- | ipc/sem.c | 10 | ||||
-rw-r--r-- | ipc/shm.c | 20 | ||||
-rw-r--r-- | ipc/util.c | 5 |
4 files changed, 43 insertions, 12 deletions
@@ -476,9 +476,9 @@ static int msgctl_info(struct ipc_namespace *ns, int msqid, | |||
476 | static int msgctl_stat(struct ipc_namespace *ns, int msqid, | 476 | static int msgctl_stat(struct ipc_namespace *ns, int msqid, |
477 | int cmd, struct msqid64_ds *p) | 477 | int cmd, struct msqid64_ds *p) |
478 | { | 478 | { |
479 | int err; | ||
480 | struct msg_queue *msq; | 479 | struct msg_queue *msq; |
481 | int success_return; | 480 | int id = 0; |
481 | int err; | ||
482 | 482 | ||
483 | memset(p, 0, sizeof(*p)); | 483 | memset(p, 0, sizeof(*p)); |
484 | 484 | ||
@@ -489,14 +489,13 @@ static int msgctl_stat(struct ipc_namespace *ns, int msqid, | |||
489 | err = PTR_ERR(msq); | 489 | err = PTR_ERR(msq); |
490 | goto out_unlock; | 490 | goto out_unlock; |
491 | } | 491 | } |
492 | success_return = msq->q_perm.id; | 492 | id = msq->q_perm.id; |
493 | } else { | 493 | } else { |
494 | msq = msq_obtain_object_check(ns, msqid); | 494 | msq = msq_obtain_object_check(ns, msqid); |
495 | if (IS_ERR(msq)) { | 495 | if (IS_ERR(msq)) { |
496 | err = PTR_ERR(msq); | 496 | err = PTR_ERR(msq); |
497 | goto out_unlock; | 497 | goto out_unlock; |
498 | } | 498 | } |
499 | success_return = 0; | ||
500 | } | 499 | } |
501 | 500 | ||
502 | err = -EACCES; | 501 | err = -EACCES; |
@@ -507,6 +506,14 @@ static int msgctl_stat(struct ipc_namespace *ns, int msqid, | |||
507 | if (err) | 506 | if (err) |
508 | goto out_unlock; | 507 | goto out_unlock; |
509 | 508 | ||
509 | ipc_lock_object(&msq->q_perm); | ||
510 | |||
511 | if (!ipc_valid_object(&msq->q_perm)) { | ||
512 | ipc_unlock_object(&msq->q_perm); | ||
513 | err = -EIDRM; | ||
514 | goto out_unlock; | ||
515 | } | ||
516 | |||
510 | kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); | 517 | kernel_to_ipc64_perm(&msq->q_perm, &p->msg_perm); |
511 | p->msg_stime = msq->q_stime; | 518 | p->msg_stime = msq->q_stime; |
512 | p->msg_rtime = msq->q_rtime; | 519 | p->msg_rtime = msq->q_rtime; |
@@ -516,9 +523,10 @@ static int msgctl_stat(struct ipc_namespace *ns, int msqid, | |||
516 | p->msg_qbytes = msq->q_qbytes; | 523 | p->msg_qbytes = msq->q_qbytes; |
517 | p->msg_lspid = msq->q_lspid; | 524 | p->msg_lspid = msq->q_lspid; |
518 | p->msg_lrpid = msq->q_lrpid; | 525 | p->msg_lrpid = msq->q_lrpid; |
519 | rcu_read_unlock(); | ||
520 | 526 | ||
521 | return success_return; | 527 | ipc_unlock_object(&msq->q_perm); |
528 | rcu_read_unlock(); | ||
529 | return id; | ||
522 | 530 | ||
523 | out_unlock: | 531 | out_unlock: |
524 | rcu_read_unlock(); | 532 | rcu_read_unlock(); |
@@ -1213,10 +1213,20 @@ static int semctl_stat(struct ipc_namespace *ns, int semid, | |||
1213 | if (err) | 1213 | if (err) |
1214 | goto out_unlock; | 1214 | goto out_unlock; |
1215 | 1215 | ||
1216 | ipc_lock_object(&sma->sem_perm); | ||
1217 | |||
1218 | if (!ipc_valid_object(&sma->sem_perm)) { | ||
1219 | ipc_unlock_object(&sma->sem_perm); | ||
1220 | err = -EIDRM; | ||
1221 | goto out_unlock; | ||
1222 | } | ||
1223 | |||
1216 | kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); | 1224 | kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); |
1217 | semid64->sem_otime = get_semotime(sma); | 1225 | semid64->sem_otime = get_semotime(sma); |
1218 | semid64->sem_ctime = sma->sem_ctime; | 1226 | semid64->sem_ctime = sma->sem_ctime; |
1219 | semid64->sem_nsems = sma->sem_nsems; | 1227 | semid64->sem_nsems = sma->sem_nsems; |
1228 | |||
1229 | ipc_unlock_object(&sma->sem_perm); | ||
1220 | rcu_read_unlock(); | 1230 | rcu_read_unlock(); |
1221 | return id; | 1231 | return id; |
1222 | 1232 | ||
@@ -909,9 +909,11 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, | |||
909 | int cmd, struct shmid64_ds *tbuf) | 909 | int cmd, struct shmid64_ds *tbuf) |
910 | { | 910 | { |
911 | struct shmid_kernel *shp; | 911 | struct shmid_kernel *shp; |
912 | int result; | 912 | int id = 0; |
913 | int err; | 913 | int err; |
914 | 914 | ||
915 | memset(tbuf, 0, sizeof(*tbuf)); | ||
916 | |||
915 | rcu_read_lock(); | 917 | rcu_read_lock(); |
916 | if (cmd == SHM_STAT) { | 918 | if (cmd == SHM_STAT) { |
917 | shp = shm_obtain_object(ns, shmid); | 919 | shp = shm_obtain_object(ns, shmid); |
@@ -919,14 +921,13 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, | |||
919 | err = PTR_ERR(shp); | 921 | err = PTR_ERR(shp); |
920 | goto out_unlock; | 922 | goto out_unlock; |
921 | } | 923 | } |
922 | result = shp->shm_perm.id; | 924 | id = shp->shm_perm.id; |
923 | } else { | 925 | } else { |
924 | shp = shm_obtain_object_check(ns, shmid); | 926 | shp = shm_obtain_object_check(ns, shmid); |
925 | if (IS_ERR(shp)) { | 927 | if (IS_ERR(shp)) { |
926 | err = PTR_ERR(shp); | 928 | err = PTR_ERR(shp); |
927 | goto out_unlock; | 929 | goto out_unlock; |
928 | } | 930 | } |
929 | result = 0; | ||
930 | } | 931 | } |
931 | 932 | ||
932 | err = -EACCES; | 933 | err = -EACCES; |
@@ -937,7 +938,14 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, | |||
937 | if (err) | 938 | if (err) |
938 | goto out_unlock; | 939 | goto out_unlock; |
939 | 940 | ||
940 | memset(tbuf, 0, sizeof(*tbuf)); | 941 | ipc_lock_object(&shp->shm_perm); |
942 | |||
943 | if (!ipc_valid_object(&shp->shm_perm)) { | ||
944 | ipc_unlock_object(&shp->shm_perm); | ||
945 | err = -EIDRM; | ||
946 | goto out_unlock; | ||
947 | } | ||
948 | |||
941 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); | 949 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); |
942 | tbuf->shm_segsz = shp->shm_segsz; | 950 | tbuf->shm_segsz = shp->shm_segsz; |
943 | tbuf->shm_atime = shp->shm_atim; | 951 | tbuf->shm_atime = shp->shm_atim; |
@@ -946,8 +954,10 @@ static int shmctl_stat(struct ipc_namespace *ns, int shmid, | |||
946 | tbuf->shm_cpid = shp->shm_cprid; | 954 | tbuf->shm_cpid = shp->shm_cprid; |
947 | tbuf->shm_lpid = shp->shm_lprid; | 955 | tbuf->shm_lpid = shp->shm_lprid; |
948 | tbuf->shm_nattch = shp->shm_nattch; | 956 | tbuf->shm_nattch = shp->shm_nattch; |
957 | |||
958 | ipc_unlock_object(&shp->shm_perm); | ||
949 | rcu_read_unlock(); | 959 | rcu_read_unlock(); |
950 | return result; | 960 | return id; |
951 | 961 | ||
952 | out_unlock: | 962 | out_unlock: |
953 | rcu_read_unlock(); | 963 | rcu_read_unlock(); |
diff --git a/ipc/util.c b/ipc/util.c index ff045fec8d83..4ed5a17dd06f 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -23,9 +23,12 @@ | |||
23 | * tree. | 23 | * tree. |
24 | * - perform initial checks (capabilities, auditing and permission, | 24 | * - perform initial checks (capabilities, auditing and permission, |
25 | * etc). | 25 | * etc). |
26 | * - perform read-only operations, such as STAT, INFO commands. | 26 | * - perform read-only operations, such as INFO command, that |
27 | * do not demand atomicity | ||
27 | * acquire the ipc lock (kern_ipc_perm.lock) through | 28 | * acquire the ipc lock (kern_ipc_perm.lock) through |
28 | * ipc_lock_object() | 29 | * ipc_lock_object() |
30 | * - perform read-only operations that demand atomicity, | ||
31 | * such as STAT command. | ||
29 | * - perform data updates, such as SET, RMID commands and | 32 | * - perform data updates, such as SET, RMID commands and |
30 | * mechanism-specific operations (semop/semtimedop, | 33 | * mechanism-specific operations (semop/semtimedop, |
31 | * msgsnd/msgrcv, shmat/shmdt). | 34 | * msgsnd/msgrcv, shmat/shmdt). |