hardening updates for v6.8-rc1

- Introduce the param_unknown_fn type and other clean ups (Andy Shevchenko)
 
 - Various __counted_by annotations (Christophe JAILLET, Gustavo A. R. Silva,
   Kees Cook)
 
 - Add KFENCE test to LKDTM (Stephen Boyd)
 
 - Various strncpy() refactorings (Justin Stitt)
 
 - Fix qnx4 to avoid writing into the smaller of two overlapping buffers
 
 - Various strlcpy() refactorings
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmWcOsQWHGtlZXNjb29r
 QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJoiDD/9gNhalNG+6MNF5TDwSvO9X7pvL
 bQ6D3clByRxYjnJ4dMQ7p3s+rJ937uQt9PezIWHgRoldjQy3x7AJ5BxkhjeMlD2B
 YLbfdVYPy09X0Ewk1Efvfm/ta6tJpBGYF7Bc7LIneZrdQ6gemBpLW1PNZAFYzcWX
 oDjV+M1NytxaiF0aebxPZvZ1W+NGQ105Sxvj5MheDoezyO/j0CTe+ZYtCzFguFY0
 8SPpR5FG4AFidb8GHd5Ndv0trVWjF1jat0FUFgEFOCE0fJNWLVR0Bbr2MtXiG7wL
 LF7IZ/Mn+mi+O3BmcD6JiaYf9EPlMUXCyqc8NvsnoWGqhWhWmQPCInZVrpplMUNK
 V/UHVMkmjDs4f/lAHBJoJHDK6fmOD+cAFaNMOltfErcjV4s+lEo6vHoiKl8hfPnH
 EzpQaK3funGroVYwTc35e07NrJJHCzqIUhZ0FJO7ByuOE2tIomiVo9Xy9gy54iCT
 qzC7zkrZ0MKqui4qiUY9FWayRRYLX4qNxELm4yie6Pzmk8943hNOaDofcyKWuZFC
 eqvhIkvqb4LasLrzCBk+ehA2KWSRmTrR6E9IygwbBXUTsvn2yj2RRYeAlGQNBTBZ
 adgSXQpRBmtKYqyihWLhP4QcunknEiQdDS3lS2qJmPH33Iv3jGH4yS6BNIBufMGL
 PoC2UxSfGd+YT079fw==
 =1Wxx
 -----END PGP SIGNATURE-----

Merge tag 'hardening-v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull hardening updates from Kees Cook:

 - Introduce the param_unknown_fn type and other clean ups (Andy
   Shevchenko)

 - Various __counted_by annotations (Christophe JAILLET, Gustavo A. R.
   Silva, Kees Cook)

 - Add KFENCE test to LKDTM (Stephen Boyd)

 - Various strncpy() refactorings (Justin Stitt)

 - Fix qnx4 to avoid writing into the smaller of two overlapping buffers

 - Various strlcpy() refactorings

* tag 'hardening-v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  qnx4: Use get_directory_fname() in qnx4_match()
  qnx4: Extract dir entry filename processing into helper
  atags_proc: Add __counted_by for struct buffer and use struct_size()
  tracing/uprobe: Replace strlcpy() with strscpy()
  params: Fix multi-line comment style
  params: Sort headers
  params: Use size_add() for kmalloc()
  params: Do not go over the limit when getting the string length
  params: Introduce the param_unknown_fn type
  lkdtm: Add kfence read after free crash type
  nvme-fc: replace deprecated strncpy with strscpy
  nvdimm/btt: replace deprecated strncpy with strscpy
  nvme-fabrics: replace deprecated strncpy with strscpy
  drm/modes: replace deprecated strncpy with strscpy_pad
  afs: Add __counted_by for struct afs_acl and use struct_size()
  VMCI: Annotate struct vmci_handle_arr with __counted_by
  i40e: Annotate struct i40e_qvlist_info with __counted_by
  HID: uhid: replace deprecated strncpy with strscpy
  samples: Replace strlcpy() with strscpy()
  SUNRPC: Replace strlcpy() with strscpy()
This commit is contained in:
Linus Torvalds 2024-01-10 11:03:52 -08:00
commit 120a201bd2
21 changed files with 210 additions and 126 deletions

View file

@ -7,7 +7,7 @@
struct buffer { struct buffer {
size_t size; size_t size;
char data[]; char data[] __counted_by(size);
}; };
static ssize_t atags_read(struct file *file, char __user *buf, static ssize_t atags_read(struct file *file, char __user *buf,
@ -54,7 +54,7 @@ static int __init init_atags_procfs(void)
WARN_ON(tag->hdr.tag != ATAG_NONE); WARN_ON(tag->hdr.tag != ATAG_NONE);
b = kmalloc(sizeof(*b) + size, GFP_KERNEL); b = kmalloc(struct_size(b, data, size), GFP_KERNEL);
if (!b) if (!b)
goto nomem; goto nomem;

View file

@ -2617,8 +2617,7 @@ void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out,
break; break;
} }
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); strscpy_pad(out->name, in->name, sizeof(out->name));
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
} }
/** /**
@ -2659,8 +2658,7 @@ int drm_mode_convert_umode(struct drm_device *dev,
* useful for the kernel->userspace direction anyway. * useful for the kernel->userspace direction anyway.
*/ */
out->type = in->type & DRM_MODE_TYPE_ALL; out->type = in->type & DRM_MODE_TYPE_ALL;
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); strscpy_pad(out->name, in->name, sizeof(out->name));
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
/* Clearing picture aspect ratio bits from out flags, /* Clearing picture aspect ratio bits from out flags,
* as the aspect-ratio information is not stored in * as the aspect-ratio information is not stored in

View file

@ -490,7 +490,7 @@ static int uhid_dev_create2(struct uhid_device *uhid,
const struct uhid_event *ev) const struct uhid_event *ev)
{ {
struct hid_device *hid; struct hid_device *hid;
size_t rd_size, len; size_t rd_size;
void *rd_data; void *rd_data;
int ret; int ret;
@ -514,13 +514,12 @@ static int uhid_dev_create2(struct uhid_device *uhid,
goto err_free; goto err_free;
} }
/* @hid is zero-initialized, strncpy() is correct, strlcpy() not */ BUILD_BUG_ON(sizeof(hid->name) != sizeof(ev->u.create2.name));
len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1; strscpy(hid->name, ev->u.create2.name, sizeof(hid->name));
strncpy(hid->name, ev->u.create2.name, len); BUILD_BUG_ON(sizeof(hid->phys) != sizeof(ev->u.create2.phys));
len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1; strscpy(hid->phys, ev->u.create2.phys, sizeof(hid->phys));
strncpy(hid->phys, ev->u.create2.phys, len); BUILD_BUG_ON(sizeof(hid->uniq) != sizeof(ev->u.create2.uniq));
len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1; strscpy(hid->uniq, ev->u.create2.uniq, sizeof(hid->uniq));
strncpy(hid->uniq, ev->u.create2.uniq, len);
hid->ll_driver = &uhid_hid_driver; hid->ll_driver = &uhid_hid_driver;
hid->bus = ev->u.create2.bus; hid->bus = ev->u.create2.bus;

View file

@ -4,6 +4,7 @@
* page allocation and slab allocations. * page allocation and slab allocations.
*/ */
#include "lkdtm.h" #include "lkdtm.h"
#include <linux/kfence.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/sched.h> #include <linux/sched.h>
@ -132,6 +133,64 @@ static void lkdtm_READ_AFTER_FREE(void)
kfree(val); kfree(val);
} }
static void lkdtm_KFENCE_READ_AFTER_FREE(void)
{
int *base, val, saw;
unsigned long timeout, resched_after;
size_t len = 1024;
/*
* The slub allocator will use the either the first word or
* the middle of the allocation to store the free pointer,
* depending on configurations. Store in the second word to
* avoid running into the freelist.
*/
size_t offset = sizeof(*base);
/*
* 100x the sample interval should be more than enough to ensure we get
* a KFENCE allocation eventually.
*/
timeout = jiffies + msecs_to_jiffies(100 * kfence_sample_interval);
/*
* Especially for non-preemption kernels, ensure the allocation-gate
* timer can catch up: after @resched_after, every failed allocation
* attempt yields, to ensure the allocation-gate timer is scheduled.
*/
resched_after = jiffies + msecs_to_jiffies(kfence_sample_interval);
do {
base = kmalloc(len, GFP_KERNEL);
if (!base) {
pr_err("FAIL: Unable to allocate kfence memory!\n");
return;
}
if (is_kfence_address(base)) {
val = 0x12345678;
base[offset] = val;
pr_info("Value in memory before free: %x\n", base[offset]);
kfree(base);
pr_info("Attempting bad read from freed memory\n");
saw = base[offset];
if (saw != val) {
/* Good! Poisoning happened, so declare a win. */
pr_info("Memory correctly poisoned (%x)\n", saw);
} else {
pr_err("FAIL: Memory was not poisoned!\n");
pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");
}
return;
}
kfree(base);
if (time_after(jiffies, resched_after))
cond_resched();
} while (time_before(jiffies, timeout));
pr_err("FAIL: kfence memory never allocated!\n");
}
static void lkdtm_WRITE_BUDDY_AFTER_FREE(void) static void lkdtm_WRITE_BUDDY_AFTER_FREE(void)
{ {
unsigned long p = __get_free_page(GFP_KERNEL); unsigned long p = __get_free_page(GFP_KERNEL);
@ -327,6 +386,7 @@ static struct crashtype crashtypes[] = {
CRASHTYPE(VMALLOC_LINEAR_OVERFLOW), CRASHTYPE(VMALLOC_LINEAR_OVERFLOW),
CRASHTYPE(WRITE_AFTER_FREE), CRASHTYPE(WRITE_AFTER_FREE),
CRASHTYPE(READ_AFTER_FREE), CRASHTYPE(READ_AFTER_FREE),
CRASHTYPE(KFENCE_READ_AFTER_FREE),
CRASHTYPE(WRITE_BUDDY_AFTER_FREE), CRASHTYPE(WRITE_BUDDY_AFTER_FREE),
CRASHTYPE(READ_BUDDY_AFTER_FREE), CRASHTYPE(READ_BUDDY_AFTER_FREE),
CRASHTYPE(SLAB_INIT_ON_ALLOC), CRASHTYPE(SLAB_INIT_ON_ALLOC),

View file

@ -17,7 +17,7 @@ struct vmci_handle_arr {
u32 max_capacity; u32 max_capacity;
u32 size; u32 size;
u32 pad; u32 pad;
struct vmci_handle entries[]; struct vmci_handle entries[] __counted_by(capacity);
}; };
#define VMCI_HANDLE_ARRAY_HEADER_SIZE \ #define VMCI_HANDLE_ARRAY_HEADER_SIZE \

View file

@ -986,7 +986,7 @@ static int btt_arena_write_layout(struct arena_info *arena)
if (!super) if (!super)
return -ENOMEM; return -ENOMEM;
strncpy(super->signature, BTT_SIG, BTT_SIG_LEN); strscpy(super->signature, BTT_SIG, sizeof(super->signature));
export_uuid(super->uuid, nd_btt->uuid); export_uuid(super->uuid, nd_btt->uuid);
export_uuid(super->parent_uuid, parent_uuid); export_uuid(super->parent_uuid, parent_uuid);
super->flags = cpu_to_le32(arena->flags); super->flags = cpu_to_le32(arena->flags);

View file

@ -387,8 +387,8 @@ static struct nvmf_connect_data *nvmf_connect_data_prep(struct nvme_ctrl *ctrl,
uuid_copy(&data->hostid, &ctrl->opts->host->id); uuid_copy(&data->hostid, &ctrl->opts->host->id);
data->cntlid = cpu_to_le16(cntlid); data->cntlid = cpu_to_le16(cntlid);
strncpy(data->subsysnqn, ctrl->opts->subsysnqn, NVMF_NQN_SIZE); strscpy(data->subsysnqn, ctrl->opts->subsysnqn, NVMF_NQN_SIZE);
strncpy(data->hostnqn, ctrl->opts->host->nqn, NVMF_NQN_SIZE); strscpy(data->hostnqn, ctrl->opts->host->nqn, NVMF_NQN_SIZE);
return data; return data;
} }

View file

@ -1218,10 +1218,10 @@ nvme_fc_connect_admin_queue(struct nvme_fc_ctrl *ctrl,
/* Linux supports only Dynamic controllers */ /* Linux supports only Dynamic controllers */
assoc_rqst->assoc_cmd.cntlid = cpu_to_be16(0xffff); assoc_rqst->assoc_cmd.cntlid = cpu_to_be16(0xffff);
uuid_copy(&assoc_rqst->assoc_cmd.hostid, &ctrl->ctrl.opts->host->id); uuid_copy(&assoc_rqst->assoc_cmd.hostid, &ctrl->ctrl.opts->host->id);
strncpy(assoc_rqst->assoc_cmd.hostnqn, ctrl->ctrl.opts->host->nqn, strscpy(assoc_rqst->assoc_cmd.hostnqn, ctrl->ctrl.opts->host->nqn,
min(FCNVME_ASSOC_HOSTNQN_LEN, NVMF_NQN_SIZE)); sizeof(assoc_rqst->assoc_cmd.hostnqn));
strncpy(assoc_rqst->assoc_cmd.subnqn, ctrl->ctrl.opts->subsysnqn, strscpy(assoc_rqst->assoc_cmd.subnqn, ctrl->ctrl.opts->subsysnqn,
min(FCNVME_ASSOC_SUBNQN_LEN, NVMF_NQN_SIZE)); sizeof(assoc_rqst->assoc_cmd.subnqn));
lsop->queue = queue; lsop->queue = queue;
lsreq->rqstaddr = assoc_rqst; lsreq->rqstaddr = assoc_rqst;

View file

@ -1213,7 +1213,7 @@ extern void afs_fs_inline_bulk_status(struct afs_operation *);
struct afs_acl { struct afs_acl {
u32 size; u32 size;
u8 data[]; u8 data[] __counted_by(size);
}; };
extern void afs_fs_fetch_acl(struct afs_operation *); extern void afs_fs_fetch_acl(struct afs_operation *);

View file

@ -75,7 +75,7 @@ static bool afs_make_acl(struct afs_operation *op,
{ {
struct afs_acl *acl; struct afs_acl *acl;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL); acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
if (!acl) { if (!acl) {
afs_op_nomem(op); afs_op_nomem(op);
return false; return false;

View file

@ -15,43 +15,6 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include "qnx4.h" #include "qnx4.h"
/*
* A qnx4 directory entry is an inode entry or link info
* depending on the status field in the last byte. The
* first byte is where the name start either way, and a
* zero means it's empty.
*
* Also, due to a bug in gcc, we don't want to use the
* real (differently sized) name arrays in the inode and
* link entries, but always the 'de_name[]' one in the
* fake struct entry.
*
* See
*
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578#c6
*
* for details, but basically gcc will take the size of the
* 'name' array from one of the used union entries randomly.
*
* This use of 'de_name[]' (48 bytes) avoids the false positive
* warnings that would happen if gcc decides to use 'inode.di_name'
* (16 bytes) even when the pointer and size were to come from
* 'link.dl_name' (48 bytes).
*
* In all cases the actual name pointer itself is the same, it's
* only the gcc internal 'what is the size of this field' logic
* that can get confused.
*/
union qnx4_directory_entry {
struct {
const char de_name[48];
u8 de_pad[15];
u8 de_status;
};
struct qnx4_inode_entry inode;
struct qnx4_link_info link;
};
static int qnx4_readdir(struct file *file, struct dir_context *ctx) static int qnx4_readdir(struct file *file, struct dir_context *ctx)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
@ -74,26 +37,25 @@ static int qnx4_readdir(struct file *file, struct dir_context *ctx)
ix = (ctx->pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK; ix = (ctx->pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK;
for (; ix < QNX4_INODES_PER_BLOCK; ix++, ctx->pos += QNX4_DIR_ENTRY_SIZE) { for (; ix < QNX4_INODES_PER_BLOCK; ix++, ctx->pos += QNX4_DIR_ENTRY_SIZE) {
union qnx4_directory_entry *de; union qnx4_directory_entry *de;
const char *fname;
offset = ix * QNX4_DIR_ENTRY_SIZE; offset = ix * QNX4_DIR_ENTRY_SIZE;
de = (union qnx4_directory_entry *) (bh->b_data + offset); de = (union qnx4_directory_entry *) (bh->b_data + offset);
if (!de->de_name[0]) fname = get_entry_fname(de, &size);
continue; if (!fname)
if (!(de->de_status & (QNX4_FILE_USED|QNX4_FILE_LINK)))
continue; continue;
if (!(de->de_status & QNX4_FILE_LINK)) { if (!(de->de_status & QNX4_FILE_LINK)) {
size = sizeof(de->inode.di_fname);
ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1; ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1;
} else { } else {
size = sizeof(de->link.dl_fname);
ino = ( le32_to_cpu(de->link.dl_inode_blk) - 1 ) * ino = ( le32_to_cpu(de->link.dl_inode_blk) - 1 ) *
QNX4_INODES_PER_BLOCK + QNX4_INODES_PER_BLOCK +
de->link.dl_inode_ndx; de->link.dl_inode_ndx;
} }
size = strnlen(de->de_name, size);
QNX4DEBUG((KERN_INFO "qnx4_readdir:%.*s\n", size, name)); QNX4DEBUG((KERN_INFO "qnx4_readdir:%.*s\n", size, fname));
if (!dir_emit(ctx, de->de_name, size, ino, DT_UNKNOWN)) { if (!dir_emit(ctx, fname, size, ino, DT_UNKNOWN)) {
brelse(bh); brelse(bh);
return 0; return 0;
} }

View file

@ -26,31 +26,24 @@
static int qnx4_match(int len, const char *name, static int qnx4_match(int len, const char *name,
struct buffer_head *bh, unsigned long *offset) struct buffer_head *bh, unsigned long *offset)
{ {
struct qnx4_inode_entry *de; union qnx4_directory_entry *de;
int namelen, thislen; const char *fname;
int fnamelen;
if (bh == NULL) { if (bh == NULL) {
printk(KERN_WARNING "qnx4: matching unassigned buffer !\n"); printk(KERN_WARNING "qnx4: matching unassigned buffer !\n");
return 0; return 0;
} }
de = (struct qnx4_inode_entry *) (bh->b_data + *offset); de = (union qnx4_directory_entry *) (bh->b_data + *offset);
*offset += QNX4_DIR_ENTRY_SIZE; *offset += QNX4_DIR_ENTRY_SIZE;
if ((de->di_status & QNX4_FILE_LINK) != 0) {
namelen = QNX4_NAME_MAX; fname = get_entry_fname(de, &fnamelen);
} else { if (!fname || len != fnamelen)
namelen = QNX4_SHORT_NAME_MAX;
}
thislen = strlen( de->di_fname );
if ( thislen > namelen )
thislen = namelen;
if (len != thislen) {
return 0; return 0;
}
if (strncmp(name, de->di_fname, len) == 0) { if (strncmp(name, fname, len) == 0)
if ((de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)) != 0) { return 1;
return 1;
}
}
return 0; return 0;
} }

View file

@ -44,3 +44,63 @@ static inline struct qnx4_inode_entry *qnx4_raw_inode(struct inode *inode)
{ {
return &qnx4_i(inode)->raw; return &qnx4_i(inode)->raw;
} }
/*
* A qnx4 directory entry is an inode entry or link info
* depending on the status field in the last byte. The
* first byte is where the name start either way, and a
* zero means it's empty.
*
* Also, due to a bug in gcc, we don't want to use the
* real (differently sized) name arrays in the inode and
* link entries, but always the 'de_name[]' one in the
* fake struct entry.
*
* See
*
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578#c6
*
* for details, but basically gcc will take the size of the
* 'name' array from one of the used union entries randomly.
*
* This use of 'de_name[]' (48 bytes) avoids the false positive
* warnings that would happen if gcc decides to use 'inode.di_name'
* (16 bytes) even when the pointer and size were to come from
* 'link.dl_name' (48 bytes).
*
* In all cases the actual name pointer itself is the same, it's
* only the gcc internal 'what is the size of this field' logic
* that can get confused.
*/
union qnx4_directory_entry {
struct {
const char de_name[48];
u8 de_pad[15];
u8 de_status;
};
struct qnx4_inode_entry inode;
struct qnx4_link_info link;
};
static inline const char *get_entry_fname(union qnx4_directory_entry *de,
int *size)
{
/* Make sure the status byte is in the same place for all structs. */
BUILD_BUG_ON(offsetof(struct qnx4_inode_entry, di_status) !=
offsetof(struct qnx4_link_info, dl_status));
BUILD_BUG_ON(offsetof(struct qnx4_inode_entry, di_status) !=
offsetof(union qnx4_directory_entry, de_status));
if (!de->de_name[0])
return NULL;
if (!(de->de_status & (QNX4_FILE_USED|QNX4_FILE_LINK)))
return NULL;
if (!(de->de_status & QNX4_FILE_LINK))
*size = sizeof(de->inode.di_fname);
else
*size = sizeof(de->link.dl_fname);
*size = strnlen(de->de_name, *size);
return de->de_name;
}

View file

@ -223,6 +223,8 @@ bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *sla
#else /* CONFIG_KFENCE */ #else /* CONFIG_KFENCE */
#define kfence_sample_interval (0)
static inline bool is_kfence_address(const void *addr) { return false; } static inline bool is_kfence_address(const void *addr) { return false; }
static inline void kfence_alloc_pool_and_metadata(void) { } static inline void kfence_alloc_pool_and_metadata(void) { }
static inline void kfence_init(void) { } static inline void kfence_init(void) { }

View file

@ -385,6 +385,8 @@ extern bool parameq(const char *name1, const char *name2);
*/ */
extern bool parameqn(const char *name1, const char *name2, size_t n); extern bool parameqn(const char *name1, const char *name2, size_t n);
typedef int (*parse_unknown_fn)(char *param, char *val, const char *doing, void *arg);
/* Called on module insert or kernel boot */ /* Called on module insert or kernel boot */
extern char *parse_args(const char *name, extern char *parse_args(const char *name,
char *args, char *args,
@ -392,9 +394,7 @@ extern char *parse_args(const char *name,
unsigned num, unsigned num,
s16 level_min, s16 level_min,
s16 level_max, s16 level_max,
void *arg, void *arg, parse_unknown_fn unknown);
int (*unknown)(char *param, char *val,
const char *doing, void *arg));
/* Called by module remove. */ /* Called by module remove. */
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS

View file

@ -45,7 +45,7 @@ struct i40e_qv_info {
struct i40e_qvlist_info { struct i40e_qvlist_info {
u32 num_vectors; u32 num_vectors;
struct i40e_qv_info qv_info[]; struct i40e_qv_info qv_info[] __counted_by(num_vectors);
}; };

View file

@ -1,19 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* Helpers for initial module or kernel cmdline parsing /*
Copyright (C) 2001 Rusty Russell. * Helpers for initial module or kernel cmdline parsing
* Copyright (C) 2001 Rusty Russell.
*/ */
#include <linux/kernel.h> #include <linux/ctype.h>
#include <linux/kstrtox.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/errno.h>
#include <linux/ctype.h> #include <linux/kernel.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/overflow.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/slab.h>
#include <linux/string.h>
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
/* Protects all built-in parameters, modules use their own param_lock */ /* Protects all built-in parameters, modules use their own param_lock */
@ -48,7 +49,7 @@ static void *kmalloc_parameter(unsigned int size)
{ {
struct kmalloced_param *p; struct kmalloced_param *p;
p = kmalloc(sizeof(*p) + size, GFP_KERNEL); p = kmalloc(size_add(sizeof(*p), size), GFP_KERNEL);
if (!p) if (!p)
return NULL; return NULL;
@ -120,9 +121,7 @@ static int parse_one(char *param,
unsigned num_params, unsigned num_params,
s16 min_level, s16 min_level,
s16 max_level, s16 max_level,
void *arg, void *arg, parse_unknown_fn handle_unknown)
int (*handle_unknown)(char *param, char *val,
const char *doing, void *arg))
{ {
unsigned int i; unsigned int i;
int err; int err;
@ -165,9 +164,7 @@ char *parse_args(const char *doing,
unsigned num, unsigned num,
s16 min_level, s16 min_level,
s16 max_level, s16 max_level,
void *arg, void *arg, parse_unknown_fn unknown)
int (*unknown)(char *param, char *val,
const char *doing, void *arg))
{ {
char *param, *val, *err = NULL; char *param, *val, *err = NULL;
@ -264,17 +261,22 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax);
int param_set_charp(const char *val, const struct kernel_param *kp) int param_set_charp(const char *val, const struct kernel_param *kp)
{ {
if (strlen(val) > 1024) { size_t len, maxlen = 1024;
len = strnlen(val, maxlen + 1);
if (len == maxlen + 1) {
pr_err("%s: string parameter too long\n", kp->name); pr_err("%s: string parameter too long\n", kp->name);
return -ENOSPC; return -ENOSPC;
} }
maybe_kfree_parameter(*(char **)kp->arg); maybe_kfree_parameter(*(char **)kp->arg);
/* This is a hack. We can't kmalloc in early boot, and we /*
* don't need to; this mangled commandline is preserved. */ * This is a hack. We can't kmalloc() in early boot, and we
* don't need to; this mangled commandline is preserved.
*/
if (slab_is_available()) { if (slab_is_available()) {
*(char **)kp->arg = kmalloc_parameter(strlen(val)+1); *(char **)kp->arg = kmalloc_parameter(len + 1);
if (!*(char **)kp->arg) if (!*(char **)kp->arg)
return -ENOMEM; return -ENOMEM;
strcpy(*(char **)kp->arg, val); strcpy(*(char **)kp->arg, val);
@ -512,7 +514,7 @@ int param_set_copystring(const char *val, const struct kernel_param *kp)
{ {
const struct kparam_string *kps = kp->str; const struct kparam_string *kps = kp->str;
if (strlen(val)+1 > kps->maxlen) { if (strnlen(val, kps->maxlen) == kps->maxlen) {
pr_err("%s: string doesn't fit in %u chars.\n", pr_err("%s: string doesn't fit in %u chars.\n",
kp->name, kps->maxlen-1); kp->name, kps->maxlen-1);
return -ENOSPC; return -ENOSPC;
@ -743,8 +745,10 @@ void module_param_sysfs_remove(struct module *mod)
{ {
if (mod->mkobj.mp) { if (mod->mkobj.mp) {
sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp); sysfs_remove_group(&mod->mkobj.kobj, &mod->mkobj.mp->grp);
/* We are positive that no one is using any param /*
* attrs at this point. Deallocate immediately. */ * We are positive that no one is using any param
* attrs at this point. Deallocate immediately.
*/
free_module_param_attrs(&mod->mkobj); free_module_param_attrs(&mod->mkobj);
} }
} }

View file

@ -151,7 +151,7 @@ fetch_store_string(unsigned long addr, void *dest, void *base)
return -ENOMEM; return -ENOMEM;
if (addr == FETCH_TOKEN_COMM) if (addr == FETCH_TOKEN_COMM)
ret = strlcpy(dst, current->comm, maxlen); ret = strscpy(dst, current->comm, maxlen);
else else
ret = strncpy_from_user(dst, src, maxlen); ret = strncpy_from_user(dst, src, maxlen);
if (ret >= 0) { if (ret >= 0) {

View file

@ -287,8 +287,14 @@ static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt,
static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)
{ {
clnt->cl_nodelen = strlcpy(clnt->cl_nodename, ssize_t copied;
nodename, sizeof(clnt->cl_nodename));
copied = strscpy(clnt->cl_nodename,
nodename, sizeof(clnt->cl_nodename));
clnt->cl_nodelen = copied < 0
? sizeof(clnt->cl_nodename) - 1
: copied;
} }
static int rpc_client_register(struct rpc_clnt *clnt, static int rpc_client_register(struct rpc_clnt *clnt,

View file

@ -305,7 +305,7 @@ TRACE_EVENT(foo_bar,
), ),
TP_fast_assign( TP_fast_assign(
strlcpy(__entry->foo, foo, 10); strscpy(__entry->foo, foo, 10);
__entry->bar = bar; __entry->bar = bar;
memcpy(__get_dynamic_array(list), lst, memcpy(__get_dynamic_array(list), lst,
__length_of(lst) * sizeof(int)); __length_of(lst) * sizeof(int));

View file

@ -291,8 +291,8 @@ static int skeleton_querycap(struct file *file, void *priv,
{ {
struct skeleton *skel = video_drvdata(file); struct skeleton *skel = video_drvdata(file);
strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
strlcpy(cap->card, "V4L2 PCI Skeleton", sizeof(cap->card)); strscpy(cap->card, "V4L2 PCI Skeleton", sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
pci_name(skel->pdev)); pci_name(skel->pdev));
return 0; return 0;
@ -597,11 +597,11 @@ static int skeleton_enum_input(struct file *file, void *priv,
i->type = V4L2_INPUT_TYPE_CAMERA; i->type = V4L2_INPUT_TYPE_CAMERA;
if (i->index == 0) { if (i->index == 0) {
i->std = SKEL_TVNORMS; i->std = SKEL_TVNORMS;
strlcpy(i->name, "S-Video", sizeof(i->name)); strscpy(i->name, "S-Video", sizeof(i->name));
i->capabilities = V4L2_IN_CAP_STD; i->capabilities = V4L2_IN_CAP_STD;
} else { } else {
i->std = 0; i->std = 0;
strlcpy(i->name, "HDMI", sizeof(i->name)); strscpy(i->name, "HDMI", sizeof(i->name));
i->capabilities = V4L2_IN_CAP_DV_TIMINGS; i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
} }
return 0; return 0;
@ -845,7 +845,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Initialize the video_device structure */ /* Initialize the video_device structure */
vdev = &skel->vdev; vdev = &skel->vdev;
strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
/* /*
* There is nothing to clean up, so release is set to an empty release * There is nothing to clean up, so release is set to an empty release
* function. The release callback must be non-NULL. * function. The release callback must be non-NULL.