Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lsm: consolidate buffer size handling into lsm_fill_user_ctx()

While we have a lsm_fill_user_ctx() helper function designed to make
life easier for LSMs which return lsm_ctx structs to userspace, we
didn't include all of the buffer length safety checks and buffer
padding adjustments in the helper. This led to code duplication
across the different LSMs and the possibility for mistakes across the
different LSM subsystems. In order to reduce code duplication and
decrease the chances of silly mistakes, we're consolidating all of
this code into the lsm_fill_user_ctx() helper.

The buffer padding is also modified from a fixed 8-byte alignment to
an alignment that matches the word length of the machine
(BITS_PER_LONG / 8).

Signed-off-by: Paul Moore <paul@paul-moore.com>

+67 -77
+5 -4
include/linux/security.h
··· 492 492 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen); 493 493 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen); 494 494 int security_locked_down(enum lockdown_reason what); 495 - int lsm_fill_user_ctx(struct lsm_ctx __user *ctx, void *context, 496 - size_t context_size, u64 id, u64 flags); 495 + int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len, 496 + void *val, size_t val_len, u64 id, u64 flags); 497 497 #else /* CONFIG_SECURITY */ 498 498 499 499 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data) ··· 1424 1424 { 1425 1425 return 0; 1426 1426 } 1427 - static inline int lsm_fill_user_ctx(struct lsm_ctx __user *ctx, void *context, 1428 - size_t context_size, u64 id, u64 flags) 1427 + static inline int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, 1428 + size_t *uctx_len, void *val, size_t val_len, 1429 + u64 id, u64 flags) 1429 1430 { 1430 1431 return -EOPNOTSUPP; 1431 1432 }
+3 -12
security/apparmor/lsm.c
··· 782 782 int error = -ENOENT; 783 783 struct aa_task_ctx *ctx = task_ctx(current); 784 784 struct aa_label *label = NULL; 785 - size_t total_len = 0; 786 785 char *value; 787 786 788 787 switch (attr) { ··· 803 804 804 805 if (label) { 805 806 error = aa_getprocattr(label, &value, false); 806 - if (error > 0) { 807 - total_len = ALIGN(struct_size(lx, ctx, error), 8); 808 - if (total_len > *size) 809 - error = -E2BIG; 810 - else if (lx) 811 - error = lsm_fill_user_ctx(lx, value, error, 812 - LSM_ID_APPARMOR, 0); 813 - else 814 - error = 1; 815 - } 807 + if (error > 0) 808 + error = lsm_fill_user_ctx(lx, size, value, error, 809 + LSM_ID_APPARMOR, 0); 816 810 kfree(value); 817 811 } 818 812 819 813 aa_put_label(label); 820 814 821 - *size = total_len; 822 815 if (error < 0) 823 816 return error; 824 817 return 1;
+31 -24
security/security.c
··· 772 772 773 773 /** 774 774 * lsm_fill_user_ctx - Fill a user space lsm_ctx structure 775 - * @ctx: an LSM context to be filled 776 - * @context: the new context value 777 - * @context_size: the size of the new context value 775 + * @uctx: a userspace LSM context to be filled 776 + * @uctx_len: available uctx size (input), used uctx size (output) 777 + * @val: the new LSM context value 778 + * @val_len: the size of the new LSM context value 778 779 * @id: LSM id 779 780 * @flags: LSM defined flags 780 781 * 781 - * Fill all of the fields in a user space lsm_ctx structure. 782 - * Caller is assumed to have verified that @ctx has enough space 783 - * for @context. 782 + * Fill all of the fields in a userspace lsm_ctx structure. 784 783 * 785 - * Returns 0 on success, -EFAULT on a copyout error, -ENOMEM 786 - * if memory can't be allocated. 784 + * Returns 0 on success, -E2BIG if userspace buffer is not large enough, 785 + * -EFAULT on a copyout error, -ENOMEM if memory can't be allocated. 787 786 */ 788 - int lsm_fill_user_ctx(struct lsm_ctx __user *ctx, void *context, 789 - size_t context_size, u64 id, u64 flags) 787 + int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len, 788 + void *val, size_t val_len, 789 + u64 id, u64 flags) 790 790 { 791 - struct lsm_ctx *lctx; 792 - size_t locallen = struct_size(lctx, ctx, context_size); 791 + struct lsm_ctx *nctx = NULL; 792 + size_t nctx_len; 793 793 int rc = 0; 794 794 795 - lctx = kzalloc(locallen, GFP_KERNEL); 796 - if (lctx == NULL) 797 - return -ENOMEM; 795 + nctx_len = ALIGN(struct_size(nctx, ctx, val_len), BITS_PER_LONG / 8); 796 + if (nctx_len > *uctx_len) { 797 + rc = -E2BIG; 798 + goto out; 799 + } 798 800 799 - lctx->id = id; 800 - lctx->flags = flags; 801 - lctx->ctx_len = context_size; 802 - lctx->len = locallen; 801 + nctx = kzalloc(nctx_len, GFP_KERNEL); 802 + if (nctx == NULL) { 803 + rc = -ENOMEM; 804 + goto out; 805 + } 806 + nctx->id = id; 807 + nctx->flags = flags; 808 + nctx->len = nctx_len; 809 + nctx->ctx_len = val_len; 810 + memcpy(nctx->ctx, val, val_len); 803 811 804 - memcpy(lctx->ctx, context, context_size); 805 - 806 - if (copy_to_user(ctx, lctx, locallen)) 812 + if (copy_to_user(uctx, nctx, nctx_len)) 807 813 rc = -EFAULT; 808 814 809 - kfree(lctx); 810 - 815 + out: 816 + kfree(nctx); 817 + *uctx_len = nctx_len; 811 818 return rc; 812 819 } 813 820
+22 -20
security/selinux/hooks.c
··· 6486 6486 return error; 6487 6487 } 6488 6488 6489 + /** 6490 + * selinux_getselfattr - Get SELinux current task attributes 6491 + * @attr: the requested attribute 6492 + * @ctx: buffer to receive the result 6493 + * @size: buffer size (input), buffer size used (output) 6494 + * @flags: unused 6495 + * 6496 + * Fill the passed user space @ctx with the details of the requested 6497 + * attribute. 6498 + * 6499 + * Returns the number of attributes on success, an error code otherwise. 6500 + * There will only ever be one attribute. 6501 + */ 6489 6502 static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx, 6490 6503 size_t *size, u32 flags) 6491 6504 { 6492 - char *value; 6493 - size_t total_len; 6494 - int len; 6495 - int rc = 0; 6505 + int rc; 6506 + char *val; 6507 + int val_len; 6496 6508 6497 - len = selinux_lsm_getattr(attr, current, &value); 6498 - if (len < 0) 6499 - return len; 6500 - 6501 - total_len = ALIGN(struct_size(ctx, ctx, len), 8); 6502 - 6503 - if (total_len > *size) 6504 - rc = -E2BIG; 6505 - else if (ctx) 6506 - rc = lsm_fill_user_ctx(ctx, value, len, LSM_ID_SELINUX, 0); 6507 - 6508 - kfree(value); 6509 - *size = total_len; 6510 - if (rc < 0) 6511 - return rc; 6512 - return 1; 6509 + val_len = selinux_lsm_getattr(attr, current, &val); 6510 + if (val_len < 0) 6511 + return val_len; 6512 + rc = lsm_fill_user_ctx(ctx, size, val, val_len, LSM_ID_SELINUX, 0); 6513 + kfree(val); 6514 + return (!rc ? 1 : rc); 6513 6515 } 6514 6516 6515 6517 static int selinux_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
+6 -17
security/smack/smack_lsm.c
··· 3642 3642 static int smack_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx, 3643 3643 size_t *size, u32 flags) 3644 3644 { 3645 - struct smack_known *skp = smk_of_current(); 3646 - int total; 3647 - int slen; 3648 3645 int rc; 3646 + struct smack_known *skp; 3649 3647 3650 3648 if (attr != LSM_ATTR_CURRENT) 3651 3649 return -EOPNOTSUPP; 3652 3650 3653 - slen = strlen(skp->smk_known) + 1; 3654 - total = ALIGN(slen + sizeof(*ctx), 8); 3655 - if (total > *size) 3656 - rc = -E2BIG; 3657 - else if (ctx) 3658 - rc = lsm_fill_user_ctx(ctx, skp->smk_known, slen, LSM_ID_SMACK, 3659 - 0); 3660 - else 3661 - rc = 1; 3662 - 3663 - *size = total; 3664 - if (rc >= 0) 3665 - return 1; 3666 - return rc; 3651 + skp = smk_of_current(); 3652 + rc = lsm_fill_user_ctx(ctx, size, 3653 + skp->smk_known, strlen(skp->smk_known) + 1, 3654 + LSM_ID_SMACK, 0); 3655 + return (!rc ? 1 : rc); 3667 3656 } 3668 3657 3669 3658 /**