Skip to content

Commit

Permalink
ipc/shm: handle removed segments gracefully in shm_mmap()
Browse files Browse the repository at this point in the history
remap_file_pages(2) emulation can reach file which represents removed
IPC ID as long as a memory segment is mapped.  It breaks expectations of
IPC subsystem.

Test case (rewritten to be more human readable, originally autogenerated
by syzkaller[1]):

	#define _GNU_SOURCE
	#include <stdlib.h>
	#include <sys/ipc.h>
	#include <sys/mman.h>
	#include <sys/shm.h>

	#define PAGE_SIZE 4096

	int main()
	{
		int id;
		void *p;

		id = shmget(IPC_PRIVATE, 3 * PAGE_SIZE, 0);
		p = shmat(id, NULL, 0);
		shmctl(id, IPC_RMID, NULL);
		remap_file_pages(p, 3 * PAGE_SIZE, 0, 7, 0);

	        return 0;
	}

The patch changes shm_mmap() and code around shm_lock() to propagate
locking error back to caller of shm_mmap().

[1] http://github.com/google/syzkaller

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Manfred Spraul <manfred@colorfullife.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
kiryl authored and torvalds committed Feb 19, 2016
1 parent 64f0085 commit 1ac0b6d
Showing 1 changed file with 43 additions and 10 deletions.
53 changes: 43 additions & 10 deletions ipc/shm.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,12 @@ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id);

/*
* We raced in the idr lookup or with shm_destroy(). Either way, the
* ID is busted.
* Callers of shm_lock() must validate the status of the returned ipc
* object pointer (as returned by ipc_lock()), and error out as
* appropriate.
*/
WARN_ON(IS_ERR(ipcp));

if (IS_ERR(ipcp))
return (void *)ipcp;
return container_of(ipcp, struct shmid_kernel, shm_perm);
}

Expand All @@ -186,18 +187,33 @@ static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
}


/* This is called by fork, once for every shm attach. */
static void shm_open(struct vm_area_struct *vma)
static int __shm_open(struct vm_area_struct *vma)
{
struct file *file = vma->vm_file;
struct shm_file_data *sfd = shm_file_data(file);
struct shmid_kernel *shp;

shp = shm_lock(sfd->ns, sfd->id);

if (IS_ERR(shp))
return PTR_ERR(shp);

shp->shm_atim = get_seconds();
shp->shm_lprid = task_tgid_vnr(current);
shp->shm_nattch++;
shm_unlock(shp);
return 0;
}

/* This is called by fork, once for every shm attach. */
static void shm_open(struct vm_area_struct *vma)
{
int err = __shm_open(vma);
/*
* We raced in the idr lookup or with shm_destroy().
* Either way, the ID is busted.
*/
WARN_ON_ONCE(err);
}

/*
Expand Down Expand Up @@ -260,13 +276,22 @@ static void shm_close(struct vm_area_struct *vma)
down_write(&shm_ids(ns).rwsem);
/* remove from the list of attaches of the shm segment */
shp = shm_lock(ns, sfd->id);

/*
* We raced in the idr lookup or with shm_destroy().
* Either way, the ID is busted.
*/
if (WARN_ON_ONCE(IS_ERR(shp)))
goto done; /* no-op */

shp->shm_lprid = task_tgid_vnr(current);
shp->shm_dtim = get_seconds();
shp->shm_nattch--;
if (shm_may_destroy(ns, shp))
shm_destroy(ns, shp);
else
shm_unlock(shp);
done:
up_write(&shm_ids(ns).rwsem);
}

Expand Down Expand Up @@ -388,17 +413,25 @@ static int shm_mmap(struct file *file, struct vm_area_struct *vma)
struct shm_file_data *sfd = shm_file_data(file);
int ret;

/*
* In case of remap_file_pages() emulation, the file can represent
* removed IPC ID: propogate shm_lock() error to caller.
*/
ret =__shm_open(vma);
if (ret)
return ret;

ret = sfd->file->f_op->mmap(sfd->file, vma);
if (ret != 0)
if (ret) {
shm_close(vma);
return ret;
}
sfd->vm_ops = vma->vm_ops;
#ifdef CONFIG_MMU
WARN_ON(!sfd->vm_ops->fault);
#endif
vma->vm_ops = &shm_vm_ops;
shm_open(vma);

return ret;
return 0;
}

static int shm_release(struct inode *ino, struct file *file)
Expand Down

0 comments on commit 1ac0b6d

Please sign in to comment.