CVE-2011-4324 (C, published 2012-06-21T23:55Z)
Description: value: The encode_share_access function in fs/nfs/nfs4xdr.c in the Linux kernel before 2.6.29 allows local users to cause a denial of service (BUG and system crash) by using the mknod system call with a pathname on an NFSv4 filesystem.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/torvalds/linux
Files changed:
fs/nfs/inode.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
include/linux/nfs_fs.h
include/linux/nfs_xdr.h
Commit message: NFSv4: Convert the open and close ops to use fmode

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>


Vulnerable code:
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
	int state;			/* State on the server (R,W, or RW) */
extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t);
extern void nfs4_close_sync(struct path *, struct nfs4_state *, mode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
		struct nfs4_state_owner *sp, int flags,
	p->o_arg.open_flags = flags,
static int can_open_cached(struct nfs4_state *state, int mode)
	switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
	if ((delegation->type & open_flags) != open_flags)
static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
	switch (open_flags) {
	nfs4_state_set_mode_locked(state, state->state | open_flags);
static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
	switch (open_flags) {
static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
	nfs_set_open_stateid_locked(state, stateid, open_flags);
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, int open_flags)
		nfs_set_open_stateid_locked(state, open_stateid, open_flags);
	update_open_stateflags(state, open_flags);
static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, int open_flags)
	open_flags &= (FMODE_READ|FMODE_WRITE);
	    (deleg_cur->type & open_flags) != open_flags)
	__update_open_stateid(state, open_stateid, &deleg_cur->stateid, open_flags);
		__update_open_stateid(state, open_stateid, NULL, open_flags);
static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags)
	if (delegation == NULL || (delegation->type & open_flags) == open_flags) {
	int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL);
		if (can_open_cached(state, open_mode)) {
			if (can_open_cached(state, open_mode)) {
				update_open_stateflags(state, open_mode);
		    !can_open_delegated(delegation, open_mode)) {
		if (update_open_stateid(state, NULL, &stateid, open_mode))
			data->o_arg.open_flags);
	opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res)
	opendata->o_arg.open_flags = openflags;
	nfs4_close_state(&opendata->path, newstate, openflags);
	int delegation_type = 0;
		nfs4_close_state(&data->path, state, data->o_arg.open_flags);
		if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL)))
		nfs4_close_state(&data->path, state, data->o_arg.open_flags);
static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
		nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
	opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred)
		status = _nfs4_do_open(dir, path, flags, sattr, cred, &res);
			if (calldata->arg.open_flags == 0)
		calldata->arg.open_flags = FMODE_READ;
		calldata->arg.open_flags = FMODE_WRITE;
	calldata->arg.open_flags = 0;
static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state)
	if (nd->intent.open.flags & FMODE_EXEC) {
	nfs4_close_sync(path, state, nd->intent.open.flags);
	state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
	nfs4_intent_set_file(nd, &path, state);
	state = nfs4_do_open(dir, &path, openflags, NULL, cred);
		nfs4_intent_set_file(nd, &path, state);
	nfs4_close_sync(&path, state, openflags);
	state = nfs4_do_open(dir, &path, flags, sattr, cred);
		status = nfs4_intent_set_file(nd, &path, state);
		nfs4_close_sync(&path, state, flags);
nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode)
	if (state->state == mode)
	if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
		if (mode & FMODE_WRITE)
	state->state = mode;
static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait)
	int newstate;
	switch (mode & (FMODE_READ | FMODE_WRITE)) {
void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode)
	__nfs4_close(path, state, mode, 0);
void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode)
	__nfs4_close(path, state, mode, 1);
static void encode_share_access(struct xdr_stream *xdr, int open_flags)
	switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
			BUG();
	encode_share_access(xdr, arg->open_flags);
	encode_share_access(xdr, arg->open_flags);
	int mode;
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode);
	int			open_flags;

Fixed code:
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
	fmode_t state;			/* State on the server (R,W, or RW) */
extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
		struct nfs4_state_owner *sp, fmode_t fmode, int flags,
	p->o_arg.open_flags = flags;
	p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)

	if (open_mode & O_EXCL)
		goto out;
	switch (mode & (FMODE_READ|FMODE_WRITE)) {
out:
static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
	if ((delegation->type & fmode) != fmode)
static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
	switch (fmode) {
	nfs4_state_set_mode_locked(state, state->state | fmode);
static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
	switch (fmode) {
static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
	nfs_set_open_stateid_locked(state, stateid, fmode);
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
		nfs_set_open_stateid_locked(state, open_stateid, fmode);
	update_open_stateflags(state, fmode);
static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
	fmode &= (FMODE_READ|FMODE_WRITE);
	    (deleg_cur->type & fmode) != fmode)
	__update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
		__update_open_stateid(state, open_stateid, NULL, fmode);
static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
	if (delegation == NULL || (delegation->type & fmode) == fmode) {
	int open_mode = opendata->o_arg.open_flags & O_EXCL;
	fmode_t fmode = opendata->o_arg.fmode;
		if (can_open_cached(state, fmode, open_mode)) {
			if (can_open_cached(state, fmode, open_mode)) {
				update_open_stateflags(state, fmode);
		    !can_open_delegated(delegation, fmode)) {
		if (update_open_stateid(state, NULL, &stateid, fmode))
			data->o_arg.fmode);
	opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL);
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
	opendata->o_arg.open_flags = 0;
	opendata->o_arg.fmode = fmode;
	nfs4_close_state(&opendata->path, newstate, fmode);
	fmode_t delegation_type = 0;
		nfs4_close_state(&data->path, state, data->o_arg.fmode);
		if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
		nfs4_close_state(&data->path, state, data->o_arg.fmode);
static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
		nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
	opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr);
static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
		status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
			if (calldata->arg.fmode == 0)
		calldata->arg.fmode = FMODE_READ;
		calldata->arg.fmode = FMODE_WRITE;
	calldata->arg.fmode = 0;
static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
	if (fmode & FMODE_EXEC) {
	nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
	fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
	state = nfs4_do_open(dir, &path, fmode, nd->intent.open.flags, &attr, cred);
	nfs4_intent_set_file(nd, &path, state, fmode);
	fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
	state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
		nfs4_intent_set_file(nd, &path, state, fmode);
	nfs4_close_sync(&path, state, fmode);
	fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
	state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
		status = nfs4_intent_set_file(nd, &path, state, fmode);
		nfs4_close_sync(&path, state, fmode);
nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
	if (state->state == fmode)
	if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
		if (fmode & FMODE_WRITE)
	state->state = fmode;
static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait)
	fmode_t newstate;
	switch (fmode & (FMODE_READ | FMODE_WRITE)) {
void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
	__nfs4_close(path, state, fmode, 0);
void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
	__nfs4_close(path, state, fmode, 1);
static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
	switch (fmode & (FMODE_READ|FMODE_WRITE)) {
			WRITE32(0);
	encode_share_access(xdr, arg->fmode);
	encode_share_access(xdr, arg->fmode);
	fmode_t mode;
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
	fmode_t			fmode;
	fmode_t			fmode;