Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0e81739 authored by David Howells's avatar David Howells Committed by James Morris
Browse files

CRED: Add some configurable debugging [try #6]

Add a config option (CONFIG_DEBUG_CREDENTIALS) to turn on some debug checking
for credential management.  The additional code keeps track of the number of
pointers from task_structs to any given cred struct, and checks to see that
this number never exceeds the usage count of the cred struct (which includes
all references, not just those from task_structs).

Furthermore, if SELinux is enabled, the code also checks that the security
pointer in the cred struct is never seen to be invalid.

This attempts to catch the bug whereby inode_has_perm() faults in an nfsd
kernel thread on seeing cred->security be a NULL pointer (it appears that the
credential struct has been previously released):

	http://www.kerneloops.org/oops.php?number=252883



Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent ed6d76e4
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
	int flags = nfsexp_flags(rqstp, exp);
	int ret;

	validate_process_creds();

	/* discard any old override before preparing the new set */
	revert_creds(get_cred(current->real_cred));
	new = prepare_creds();
@@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
	else
		new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
							new->cap_permitted);
	validate_process_creds();
	put_cred(override_creds(new));
	put_cred(new);
	validate_process_creds();
	return 0;

oom:
+2 −0
Original line number Diff line number Diff line
@@ -496,7 +496,9 @@ nfsd(void *vrqstp)
		/* Lock the export hash tables for reading. */
		exp_readlock();

		validate_process_creds();
		svc_process(rqstp);
		validate_process_creds();

		/* Unlock export hash tables */
		exp_readunlock();
+3 −0
Original line number Diff line number Diff line
@@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
	__be32		err;
	int		host_err;

	validate_process_creds();

	/*
	 * If we get here, then the client has already done an "open",
	 * and (hopefully) checked permission - so allow OWNER_OVERRIDE
@@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
out_nfserr:
	err = nfserrno(host_err);
out:
	validate_process_creds();
	return err;
}

+2 −0
Original line number Diff line number Diff line
@@ -959,6 +959,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
	int error;
	struct file *f;

	validate_creds(cred);

	/*
	 * We must always pass in a valid mount pointer.   Historically
	 * callers got away with not passing it, but we must enforce this at
+64 −1
Original line number Diff line number Diff line
@@ -114,6 +114,13 @@ struct thread_group_cred {
 */
struct cred {
	atomic_t	usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
	atomic_t	subscribers;	/* number of processes subscribed */
	void		*put_addr;
	unsigned	magic;
#define CRED_MAGIC	0x43736564
#define CRED_MAGIC_DEAD	0x44656144
#endif
	uid_t		uid;		/* real UID of the task */
	gid_t		gid;		/* real GID of the task */
	uid_t		suid;		/* saved UID of the task */
@@ -143,6 +150,7 @@ struct cred {
};

extern void __put_cred(struct cred *);
extern void exit_creds(struct task_struct *);
extern int copy_creds(struct task_struct *, unsigned long);
extern struct cred *prepare_creds(void);
extern struct cred *prepare_exec_creds(void);
@@ -158,6 +166,60 @@ extern int set_security_override_from_ctx(struct cred *, const char *);
extern int set_create_files_as(struct cred *, struct inode *);
extern void __init cred_init(void);

/*
 * check for validity of credentials
 */
#ifdef CONFIG_DEBUG_CREDENTIALS
extern void __invalid_creds(const struct cred *, const char *, unsigned);
extern void __validate_process_creds(struct task_struct *,
				     const char *, unsigned);

static inline bool creds_are_invalid(const struct cred *cred)
{
	if (cred->magic != CRED_MAGIC)
		return true;
	if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
		return true;
#ifdef CONFIG_SECURITY_SELINUX
	if ((unsigned long) cred->security < PAGE_SIZE)
		return true;
	if ((*(u32*)cred->security & 0xffffff00) ==
	    (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
		return true;
#endif
	return false;
}

static inline void __validate_creds(const struct cred *cred,
				    const char *file, unsigned line)
{
	if (unlikely(creds_are_invalid(cred)))
		__invalid_creds(cred, file, line);
}

#define validate_creds(cred)				\
do {							\
	__validate_creds((cred), __FILE__, __LINE__);	\
} while(0)

#define validate_process_creds()				\
do {								\
	__validate_process_creds(current, __FILE__, __LINE__);	\
} while(0)

extern void validate_creds_for_do_exit(struct task_struct *);
#else
static inline void validate_creds(const struct cred *cred)
{
}
static inline void validate_creds_for_do_exit(struct task_struct *tsk)
{
}
static inline void validate_process_creds(void)
{
}
#endif

/**
 * get_new_cred - Get a reference on a new set of credentials
 * @cred: The new credentials to reference
@@ -187,6 +249,7 @@ static inline struct cred *get_new_cred(struct cred *cred)
static inline const struct cred *get_cred(const struct cred *cred)
{
	struct cred *nonconst_cred = (struct cred *) cred;
	validate_creds(cred);
	return get_new_cred(nonconst_cred);
}

@@ -205,7 +268,7 @@ static inline void put_cred(const struct cred *_cred)
{
	struct cred *cred = (struct cred *) _cred;

	BUG_ON(atomic_read(&(cred)->usage) <= 0);
	validate_creds(cred);
	if (atomic_dec_and_test(&(cred)->usage))
		__put_cred(cred);
}
Loading