mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
hardening updates for v7.4-rc1
- Provide kmalloc_obj*-style API for devm_kmalloc() - Improve seq_buf API for saner C String handling with several bug fixes along the way (Kees Cook, Bill Wendling) - Overflow helper readability improvements (Bill Wendling) - Unexport native_write_cr4() (Dennis Chen) - um: fix CONFIG_GCOV for built-in code (Johannes Berg) - Improve stackinit test coverage (Justin Stitt) - KMSAN: fix memset() when using fortify-source, again (Arnd Bergmann) - lkdtm: use kmalloc() instead of __get_free_page (Mahad Ibrahim) - signalfd: don't dequeue the forced fatal signals (Oleg Nesterov) - Fix randstruct to recognize new container_of() behavior - coccinelle: kmalloc_obj: Convert multi-byte integral allocations too - Fix a couple remaining allocation return type mismatches -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCatpUqAAKCRA2KwveOeQk u6f7AQCmaAF+jGVxeljdguKRrge+bjxpsK+RIyDzf7JMUauxPwD/RvGklH+QCACS xj4tExbgBFOAo48uvqfUiGsJLaCJAQg= =uEMy -----END PGP SIGNATURE----- Merge tag 'hardening-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull hardening updates from Kees Cook: - Provide kmalloc_obj*-style API for devm_kmalloc() - Improve seq_buf API for saner C String handling with several bug fixes along the way (Kees Cook, Bill Wendling) - Overflow helper readability improvements (Bill Wendling) - Unexport native_write_cr4() (Dennis Chen) - um: fix CONFIG_GCOV for built-in code (Johannes Berg) - Improve stackinit test coverage (Justin Stitt) - KMSAN: fix memset() when using fortify-source, again (Arnd Bergmann) - lkdtm: use kmalloc() instead of __get_free_page (Mahad Ibrahim) - signalfd: don't dequeue the forced fatal signals (Oleg Nesterov) - Fix randstruct to recognize new container_of() behavior - coccinelle: kmalloc_obj: Convert multi-byte integral allocations too - Fix a couple remaining allocation return type mismatches * tag 'hardening-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (29 commits) overflow: Match arithmetic in both parts of __builtin_choose_expr lkdtm: Make UNSET_SMEP built-in-only and unexport native_write_cr4() extcon: realtek: Remove const from type_c_cfg allocation type coresight: cti: Add const to con_groups allocation type docs: core-api: Document the seq_buf API nvdimm: ndtest: Return the string length from flags_show() powerpc/papr_scm: Return the string length from the sysfs show functions seq_buf: Add seq_buf_init_append() bpf: Remove dead newline stripping from format_disasm_line() seq_buf: Add seq_buf_terminate() seq_buf: Add seq_buf_strlen() seq_buf: Clear what a writer did not claim when a seq_buf overflows seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow scsi: lpfc: Print rx monitor records straight into the seq_buf seq_buf: Do not pop from an overflowed seq_buf seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() .get_maintainer.ignore: Add Kees Cook devres: Provide kmalloc_obj*-style API for devm_kmalloc mm: Move default_gfp() into gfp_types.h kunit/stackinit: Clean up XFAIL under instrumentation ...
This commit is contained in:
commit
864c07de0c
|
|
@ -6,6 +6,7 @@ Christoph Hellwig <hch@lst.de>
|
|||
Jeff Kirsher <jeffrey.t.kirsher@intel.com>
|
||||
Johannes Berg <johannes.berg@intel.com>
|
||||
Johannes Berg <johannes@sipsolutions.net>
|
||||
Kees Cook <kees@kernel.org>
|
||||
Marc Gonzalez <marc.w.gonzalez@free.fr>
|
||||
Nathan Chancellor <nathan@kernel.org>
|
||||
Ralf Baechle <ralf@linux-mips.org>
|
||||
|
|
|
|||
|
|
@ -96,6 +96,15 @@ Error Pointers
|
|||
.. kernel-doc:: include/linux/err.h
|
||||
:internal:
|
||||
|
||||
Sequence Buffers
|
||||
----------------
|
||||
|
||||
.. kernel-doc:: include/linux/seq_buf.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: lib/seq_buf.c
|
||||
:no-identifiers: seq_buf_can_fit
|
||||
|
||||
Sorting
|
||||
-------
|
||||
|
||||
|
|
|
|||
|
|
@ -14231,6 +14231,7 @@ F: include/linux/ucopysize.h
|
|||
F: kernel/configs/hardening.config
|
||||
F: kernel/kstack_erase.c
|
||||
F: lib/tests/randstruct_kunit.c
|
||||
F: lib/tests/stackinit_kunit.c
|
||||
F: lib/tests/usercopy_kunit.c
|
||||
F: mm/usercopy.c
|
||||
F: scripts/Makefile.kstack_erase
|
||||
|
|
|
|||
|
|
@ -1108,7 +1108,7 @@ static ssize_t perf_stats_show(struct device *dev,
|
|||
|
||||
free_stats:
|
||||
kfree(stats);
|
||||
return rc ? rc : (ssize_t)seq_buf_used(&s);
|
||||
return rc ?: (ssize_t)seq_buf_strlen(&s);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(perf_stats);
|
||||
|
||||
|
|
@ -1147,10 +1147,10 @@ static ssize_t flags_show(struct device *dev,
|
|||
if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
|
||||
seq_buf_printf(&s, "scrubbed locked ");
|
||||
|
||||
if (seq_buf_used(&s))
|
||||
if (seq_buf_strlen(&s))
|
||||
seq_buf_printf(&s, "\n");
|
||||
|
||||
return seq_buf_used(&s);
|
||||
return seq_buf_strlen(&s);
|
||||
}
|
||||
DEVICE_ATTR_RO(flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
.fini_array : {
|
||||
__fini_array_start = .;
|
||||
*(.fini_array)
|
||||
*(.fini_array.*)
|
||||
__fini_array_end = .;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -490,9 +490,6 @@ void __no_profile native_write_cr4(unsigned long val)
|
|||
bits_changed);
|
||||
}
|
||||
}
|
||||
#if IS_MODULE(CONFIG_LKDTM)
|
||||
EXPORT_SYMBOL_GPL(native_write_cr4);
|
||||
#endif
|
||||
|
||||
void cr4_update_irqsoff(unsigned long set, unsigned long clear)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1368,7 +1368,8 @@ static int extcon_rtk_type_c_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), GFP_KERNEL);
|
||||
type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c->type_c_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!type_c->type_c_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -1132,7 +1132,7 @@ static int cti_create_cons_groups(struct device *dev, struct cti_device *ctidev)
|
|||
/* nr groups = dynamic + static + NULL terminator */
|
||||
nr_groups = ctidev->nr_trig_con + CORESIGHT_CTI_STATIC_GROUPS_MAX;
|
||||
ctidev->con_groups = devm_kcalloc(dev, nr_groups,
|
||||
sizeof(struct attribute_group *),
|
||||
sizeof(*ctidev->con_groups),
|
||||
GFP_KERNEL);
|
||||
if (!ctidev->con_groups)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -676,7 +676,7 @@ static void lkdtm_STACK_GUARD_PAGE_TRAILING(void)
|
|||
|
||||
static void lkdtm_UNSET_SMEP(void)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_UML)
|
||||
#if IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_UML) && IS_BUILTIN(CONFIG_LKDTM)
|
||||
#define MOV_CR4_DEPTH 64
|
||||
void (*direct_write_cr4)(unsigned long val);
|
||||
unsigned char *insn;
|
||||
|
|
@ -737,7 +737,7 @@ static void lkdtm_UNSET_SMEP(void)
|
|||
native_write_cr4(cr4);
|
||||
}
|
||||
#else
|
||||
pr_err("XFAIL: this test is x86_64-only\n");
|
||||
pr_err("XFAIL: this test is x86_64-only and requires CONFIG_LKDTM=y\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -236,11 +236,11 @@ static ssize_t lkdtm_debugfs_entry(struct file *f,
|
|||
if (count >= PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, user_buf, count)) {
|
||||
free_page((unsigned long) buf);
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
/* NULL-terminate and remove enter */
|
||||
|
|
@ -248,7 +248,7 @@ static ssize_t lkdtm_debugfs_entry(struct file *f,
|
|||
strim(buf);
|
||||
|
||||
crashtype = find_crashtype(buf);
|
||||
free_page((unsigned long)buf);
|
||||
kfree(buf);
|
||||
|
||||
if (!crashtype)
|
||||
return -EINVAL;
|
||||
|
|
@ -271,7 +271,7 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
|
|||
ssize_t out;
|
||||
char *buf;
|
||||
|
||||
buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
|
|||
|
||||
out = simple_read_from_buffer(user_buf, count, off,
|
||||
buf, n);
|
||||
free_page((unsigned long) buf);
|
||||
kfree(buf);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
@ -313,11 +313,11 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
|||
if (count < 1)
|
||||
return -EINVAL;
|
||||
|
||||
buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, user_buf, count)) {
|
||||
free_page((unsigned long) buf);
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
/* NULL-terminate and remove enter */
|
||||
|
|
@ -325,7 +325,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
|||
strim(buf);
|
||||
|
||||
crashtype = find_crashtype(buf);
|
||||
free_page((unsigned long) buf);
|
||||
kfree(buf);
|
||||
if (!crashtype)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -8108,7 +8108,6 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
|
|||
spinlock_t *ring_lock = &rx_monitor->lock;
|
||||
u32 ring_size = rx_monitor->entries;
|
||||
u32 cnt = 0;
|
||||
char tmp[DBG_LOG_STR_SZ] = {0};
|
||||
bool log_to_kmsg = (!buf || !buf_len) ? true : false;
|
||||
struct seq_buf s;
|
||||
|
||||
|
|
@ -8133,27 +8132,37 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
|
|||
|
||||
/* Read out this entry's data. */
|
||||
if (!log_to_kmsg) {
|
||||
unsigned int len;
|
||||
|
||||
/* A header that did not fit leaves no room. */
|
||||
if (seq_buf_has_overflowed(&s))
|
||||
break;
|
||||
len = seq_buf_used(&s);
|
||||
|
||||
seq_buf_printf(&s,
|
||||
"%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n",
|
||||
*head_idx,
|
||||
entry->max_bytes_per_interval,
|
||||
entry->cmf_bytes,
|
||||
entry->total_bytes,
|
||||
entry->rcv_bytes,
|
||||
entry->avg_io_latency,
|
||||
entry->avg_io_size,
|
||||
entry->max_read_cnt,
|
||||
entry->cmf_busy, entry->io_cnt,
|
||||
entry->cmf_info,
|
||||
entry->timer_utilization,
|
||||
entry->timer_interval);
|
||||
|
||||
/*
|
||||
* Drop a record whole if it does not fit, without
|
||||
* consuming its ring entry.
|
||||
* consuming its ring entry: the reader takes the
|
||||
* string up to its NUL.
|
||||
*/
|
||||
scnprintf(tmp, sizeof(tmp),
|
||||
"%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n",
|
||||
*head_idx,
|
||||
entry->max_bytes_per_interval,
|
||||
entry->cmf_bytes,
|
||||
entry->total_bytes,
|
||||
entry->rcv_bytes,
|
||||
entry->avg_io_latency,
|
||||
entry->avg_io_size,
|
||||
entry->max_read_cnt,
|
||||
entry->cmf_busy, entry->io_cnt,
|
||||
entry->cmf_info,
|
||||
entry->timer_utilization,
|
||||
entry->timer_interval);
|
||||
|
||||
if (seq_buf_puts(&s, tmp) < 0)
|
||||
if (seq_buf_has_overflowed(&s)) {
|
||||
buf[len] = '\0';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
|
||||
"4410 %02u: MBPI %llu Xmit %llu "
|
||||
|
|
|
|||
|
|
@ -48,17 +48,30 @@ static int signalfd_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void refine_sigmask(struct signalfd_ctx *ctx, sigset_t *sigmask)
|
||||
{
|
||||
struct k_sigaction *k = current->sighand->action;
|
||||
int n;
|
||||
|
||||
*sigmask = ctx->sigmask;
|
||||
for (n = 1; n <= _NSIG; ++n, ++k) {
|
||||
if (k->sa.sa_flags & SA_IMMUTABLE)
|
||||
sigaddset(sigmask, n);
|
||||
}
|
||||
}
|
||||
|
||||
static __poll_t signalfd_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct signalfd_ctx *ctx = file->private_data;
|
||||
__poll_t events = 0;
|
||||
sigset_t sigmask;
|
||||
|
||||
poll_wait(file, ¤t->sighand->signalfd_wqh, wait);
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
if (next_signal(¤t->pending, &ctx->sigmask) ||
|
||||
next_signal(¤t->signal->shared_pending,
|
||||
&ctx->sigmask))
|
||||
refine_sigmask(ctx, &sigmask);
|
||||
if (next_signal(¤t->pending, &sigmask) ||
|
||||
next_signal(¤t->signal->shared_pending, &sigmask))
|
||||
events |= EPOLLIN;
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
|
|
@ -155,11 +168,13 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
|
|||
int nonblock)
|
||||
{
|
||||
enum pid_type type;
|
||||
ssize_t ret;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
sigset_t sigmask;
|
||||
ssize_t ret;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
ret = dequeue_signal(&ctx->sigmask, info, &type);
|
||||
refine_sigmask(ctx, &sigmask);
|
||||
ret = dequeue_signal(&sigmask, info, &type);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
if (!nonblock)
|
||||
|
|
@ -174,7 +189,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
|
|||
add_wait_queue(¤t->sighand->signalfd_wqh, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
ret = dequeue_signal(&ctx->sigmask, info, &type);
|
||||
ret = dequeue_signal(&sigmask, info, &type);
|
||||
if (ret != 0)
|
||||
break;
|
||||
if (signal_pending(current)) {
|
||||
|
|
@ -184,6 +199,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
|
|||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
schedule();
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
refine_sigmask(ctx, &sigmask);
|
||||
}
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,108 @@ static inline void *devm_kcalloc(struct device *dev, size_t n, size_t size, gfp_
|
|||
{
|
||||
return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* __devm_alloc_objs - Device-managed allocation of objects of a given type
|
||||
* @DEVM_ALLOC: which device-managed size-based allocator to use.
|
||||
* @DEV: device to scope the allocation lifetime to.
|
||||
* @GFP: GFP flags for the allocation.
|
||||
* @TYPE: type to allocate space for.
|
||||
* @COUNT: how many @TYPE objects to allocate.
|
||||
*
|
||||
* Device-managed counterpart of __alloc_objs().
|
||||
* @DEVM_ALLOC takes (@DEV, size, gfp).
|
||||
*
|
||||
* Returns: Newly allocated pointer to (first) @TYPE of @COUNT-many allocated
|
||||
* @TYPE objects, or NULL on failure.
|
||||
*/
|
||||
#define __devm_alloc_objs(DEVM_ALLOC, DEV, GFP, TYPE, COUNT) \
|
||||
({ \
|
||||
const size_t __obj_size = size_mul(sizeof(TYPE), COUNT); \
|
||||
(TYPE *)DEVM_ALLOC(DEV, __obj_size, GFP); \
|
||||
})
|
||||
|
||||
/**
|
||||
* devm_kmalloc_obj - Device-managed allocation of a single typed object
|
||||
* @DEV: Device to scope the allocation lifetime to.
|
||||
* @VAR_OR_TYPE: Variable or type to allocate.
|
||||
* @GFP: Optional GFP flags (defaults to %GFP_KERNEL).
|
||||
*
|
||||
* Device-managed counterpart of kmalloc_obj(): the allocation is sized from
|
||||
* @VAR_OR_TYPE and returned as a pointer to that type, not void *.
|
||||
*
|
||||
* Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL on
|
||||
* failure.
|
||||
*/
|
||||
#define devm_kmalloc_obj(DEV, VAR_OR_TYPE, ...) \
|
||||
__devm_alloc_objs(devm_kmalloc, DEV, default_gfp(__VA_ARGS__), \
|
||||
typeof(VAR_OR_TYPE), 1)
|
||||
|
||||
/**
|
||||
* devm_kmalloc_objs - Device-managed allocation of an array of a typed object
|
||||
* @DEV: Device to scope the allocation lifetime to.
|
||||
* @VAR_OR_TYPE: Variable or type to allocate an array of.
|
||||
* @COUNT: How many elements in the array.
|
||||
* @GFP: Optional GFP flags (defaults to %GFP_KERNEL).
|
||||
*
|
||||
* Returns: newly allocated pointer to an array of @VAR_OR_TYPE on success, or
|
||||
* NULL on failure (including when the total size overflows).
|
||||
*/
|
||||
#define devm_kmalloc_objs(DEV, VAR_OR_TYPE, COUNT, ...) \
|
||||
__devm_alloc_objs(devm_kmalloc, DEV, default_gfp(__VA_ARGS__), \
|
||||
typeof(VAR_OR_TYPE), COUNT)
|
||||
|
||||
/**
|
||||
* __devm_alloc_flex - Device-managed allocation of a trailing-flex-array object
|
||||
* @DEVM_ALLOC: which device-managed allocator to use.
|
||||
* @DEV: device to scope the allocation lifetime to.
|
||||
* @GFP: GFP flags for the allocation.
|
||||
* @TYPE: type of structure to allocate space for.
|
||||
* @FAM: name of the flexible array member of @TYPE.
|
||||
* @COUNT: how many @FAM elements to allocate space for.
|
||||
*
|
||||
* Device-managed counterpart of __alloc_flex().
|
||||
* @DEVM_ALLOC takes (@DEV, size, gfp).
|
||||
*
|
||||
* Returns: Newly allocated pointer to @TYPE with @COUNT-many trailing @FAM
|
||||
* elements, or NULL on failure.
|
||||
*/
|
||||
#define __devm_alloc_flex(DEVM_ALLOC, DEV, GFP, TYPE, FAM, COUNT) \
|
||||
({ \
|
||||
const size_t __count = (COUNT); \
|
||||
const size_t __obj_size = struct_size_t(TYPE, FAM, __count); \
|
||||
TYPE *__obj_ptr = DEVM_ALLOC(DEV, __obj_size, GFP); \
|
||||
if (__obj_ptr) \
|
||||
__set_flex_counter(__obj_ptr->FAM, __count); \
|
||||
__obj_ptr; \
|
||||
})
|
||||
|
||||
/**
|
||||
* devm_kmalloc_flex - Device-managed allocation of a flexible-array structure
|
||||
* @DEV: Device to scope the allocation lifetime to.
|
||||
* @VAR_OR_TYPE: Variable or type to allocate (with its flexible array).
|
||||
* @FAM: The name of the flexible array member of the structure.
|
||||
* @COUNT: How many flexible array member elements to allocate.
|
||||
* @GFP: Optional GFP flags (defaults to %GFP_KERNEL).
|
||||
*
|
||||
* Returns: newly allocated pointer to @VAR_OR_TYPE on success, or NULL on
|
||||
* failure. If @FAM has been annotated with __counted_by(), its counter is set
|
||||
* to @COUNT.
|
||||
*/
|
||||
#define devm_kmalloc_flex(DEV, VAR_OR_TYPE, FAM, COUNT, ...) \
|
||||
__devm_alloc_flex(devm_kmalloc, DEV, default_gfp(__VA_ARGS__), \
|
||||
typeof(VAR_OR_TYPE), FAM, COUNT)
|
||||
|
||||
/* All devm_kzalloc aliases for devm_kmalloc_(obj|objs|flex). */
|
||||
#define devm_kzalloc_obj(DEV, P, ...) \
|
||||
__devm_alloc_objs(devm_kzalloc, DEV, default_gfp(__VA_ARGS__), typeof(P), 1)
|
||||
#define devm_kzalloc_objs(DEV, P, COUNT, ...) \
|
||||
__devm_alloc_objs(devm_kzalloc, DEV, default_gfp(__VA_ARGS__), \
|
||||
typeof(P), COUNT)
|
||||
#define devm_kzalloc_flex(DEV, P, FAM, COUNT, ...) \
|
||||
__devm_alloc_flex(devm_kzalloc, DEV, default_gfp(__VA_ARGS__), \
|
||||
typeof(P), FAM, COUNT)
|
||||
|
||||
static inline __realloc_size(3, 4) void * __must_check
|
||||
devm_krealloc_array(struct device *dev, void *p, size_t new_n, size_t new_size, gfp_t flags)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -458,10 +458,8 @@ __FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size,
|
|||
* __struct_size() vs __member_size() must be captured here to avoid
|
||||
* evaluating argument side-effects further into the macro layers.
|
||||
*/
|
||||
#ifndef CONFIG_KMSAN
|
||||
#define memset(p, c, s) __fortify_memset_chk(p, c, s, \
|
||||
__struct_size(p), __member_size(p))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* To make sure the compiler can enforce protection against buffer overflows,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@
|
|||
struct vm_area_struct;
|
||||
struct mempolicy;
|
||||
|
||||
/* Helper macro to avoid gfp flags if they are the default one */
|
||||
#define __default_gfp(a,b,...) b
|
||||
#define default_gfp(...) __default_gfp(,##__VA_ARGS__,GFP_KERNEL)
|
||||
|
||||
static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags)
|
||||
{
|
||||
return !!(gfp_flags & __GFP_DIRECT_RECLAIM);
|
||||
|
|
|
|||
|
|
@ -382,4 +382,8 @@ enum {
|
|||
__GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM)
|
||||
#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM)
|
||||
|
||||
/* Helper macro to avoid gfp flags if they are the default one */
|
||||
#define __default_gfp(a,b,...) b
|
||||
#define default_gfp(...) __default_gfp(,##__VA_ARGS__,GFP_KERNEL)
|
||||
|
||||
#endif /* __LINUX_GFP_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ static __always_inline size_t __must_check size_sub(size_t minuend, size_t subtr
|
|||
#define flex_array_size(p, member, count) \
|
||||
__builtin_choose_expr(__is_constexpr(count), \
|
||||
(count) * sizeof(*(p)->member) + __must_be_array((p)->member), \
|
||||
size_mul(count, sizeof(*(p)->member) + __must_be_array((p)->member)))
|
||||
size_mul(count, sizeof(*(p)->member)) + __must_be_array((p)->member))
|
||||
|
||||
/**
|
||||
* struct_size() - Calculate size of structure with trailing flexible array.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <linux/bug.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
|
|
@ -30,6 +31,10 @@ struct seq_buf {
|
|||
.size = SIZE, \
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_clear - reset the seq_buf to be read / appended from the beginning
|
||||
* @s: the seq_buf handle
|
||||
*/
|
||||
static inline void seq_buf_clear(struct seq_buf *s)
|
||||
{
|
||||
s->len = 0;
|
||||
|
|
@ -37,6 +42,14 @@ static inline void seq_buf_clear(struct seq_buf *s)
|
|||
s->buffer[0] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_init - initialize a seq_buf
|
||||
* @s: the seq_buf handle
|
||||
* @buf: pointer to the buffer
|
||||
* @size: total size of @buf
|
||||
*
|
||||
* The contents of the buffer are ignored.
|
||||
*/
|
||||
static inline void
|
||||
seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
|
||||
{
|
||||
|
|
@ -55,12 +68,46 @@ seq_buf_has_overflowed(struct seq_buf *s)
|
|||
return s->len > s->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark @s as overflowed, which discards the length of what it holds. The
|
||||
* bytes up to its last one are the string from then on, as that is where
|
||||
* seq_buf_str() terminates it, so clear whatever was not written: a writer
|
||||
* sets len to how much it filled, and anything past that was never adopted.
|
||||
*/
|
||||
static inline void
|
||||
seq_buf_set_overflow(struct seq_buf *s)
|
||||
{
|
||||
if (s->len < s->size)
|
||||
memset(s->buffer + s->len, 0, s->size - s->len);
|
||||
|
||||
s->len = s->size + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_init_append - initialize a seq_buf over a buffer that may
|
||||
* already hold NUL-terminated content
|
||||
* @s: the seq_buf handle
|
||||
* @buf: pointer to the (possibly non-empty) buffer
|
||||
* @size: total size of @buf
|
||||
*
|
||||
* Unlike seq_buf_init(), which always starts @buf at len=0, this
|
||||
* preserves whatever NUL-terminated content @buf already holds and
|
||||
* positions @s to append after it. Useful for converting code that used
|
||||
* to append to an existing buffer with strlcat()/scnprintf() and friends.
|
||||
*
|
||||
* If @buf holds no NUL within @size, @s starts out overflowed, as
|
||||
* strlcat() treats such a buffer as already truncated.
|
||||
*/
|
||||
static inline void
|
||||
seq_buf_init_append(struct seq_buf *s, char *buf, unsigned int size)
|
||||
{
|
||||
s->buffer = buf;
|
||||
s->size = size;
|
||||
s->len = strnlen(buf, size);
|
||||
if (s->len == size)
|
||||
seq_buf_set_overflow(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* How much buffer is left on the seq_buf?
|
||||
*/
|
||||
|
|
@ -79,6 +126,27 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
|
|||
return min(s->len, s->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* NUL-terminate the buffer in @s: directly after the data when there is
|
||||
* room for it, otherwise in the last byte of the buffer. @s->size must not
|
||||
* be zero.
|
||||
*
|
||||
* Returns: the offset of the NUL.
|
||||
*/
|
||||
static inline size_t __seq_buf_terminate(struct seq_buf *s)
|
||||
{
|
||||
size_t end;
|
||||
|
||||
if (seq_buf_buffer_left(s))
|
||||
end = s->len;
|
||||
else
|
||||
end = s->size - 1;
|
||||
|
||||
s->buffer[end] = 0;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_str - get NUL-terminated C string from seq_buf
|
||||
* @s: the seq_buf handle
|
||||
|
|
@ -88,7 +156,14 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
|
|||
*
|
||||
* Note, if this is called when the buffer has overflowed, then
|
||||
* the last byte of the buffer is zeroed, and the len will still
|
||||
* point passed it.
|
||||
* point passed it. The same happens when the buffer is exactly
|
||||
* full: the NUL takes the place of the last byte written, which is
|
||||
* lost, though seq_buf_used() still counts it.
|
||||
*
|
||||
* A zero-sized seq_buf has nowhere to put a NUL, so the empty string
|
||||
* is returned instead of writing to @s->buffer. Any other seq_buf
|
||||
* returns @s->buffer, even when it holds an empty string, so callers
|
||||
* always get their own buffer back.
|
||||
*
|
||||
* After this function is called, s->buffer is safe to use
|
||||
* in string operations.
|
||||
|
|
@ -97,17 +172,67 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
|
|||
*/
|
||||
static inline const char *seq_buf_str(struct seq_buf *s)
|
||||
{
|
||||
if (WARN_ON(s->size == 0))
|
||||
if (s->size == 0)
|
||||
return "";
|
||||
|
||||
if (seq_buf_buffer_left(s))
|
||||
s->buffer[s->len] = 0;
|
||||
else
|
||||
s->buffer[s->size - 1] = 0;
|
||||
__seq_buf_terminate(s);
|
||||
|
||||
return s->buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_strlen - get the length of the NUL-terminated C string in seq_buf
|
||||
* @s: the seq_buf handle
|
||||
*
|
||||
* This makes sure that the buffer in @s is NUL-terminated, exactly as
|
||||
* seq_buf_str() does, and returns the length of the resulting string
|
||||
* without walking it. Unlike seq_buf_used(), this does not count the byte
|
||||
* given up to the NUL when the buffer is full or has overflowed. When the
|
||||
* buffer is exactly full, that byte is the last one written, and calling
|
||||
* either function loses it.
|
||||
*
|
||||
* A zero-sized seq_buf holds no string, so 0 is returned without writing
|
||||
* to @s->buffer, matching what seq_buf_str() returns for one.
|
||||
*
|
||||
* After this function is called, s->buffer is safe to use
|
||||
* in string operations.
|
||||
*
|
||||
* Returns: the offset of the NUL that terminates @s->buffer. That is the
|
||||
* length of the string unless an earlier NUL is in the way, either one the
|
||||
* data written to @s carried itself, or one seq_buf_set_overflow() left
|
||||
* behind when it cleared what no writer had claimed.
|
||||
*/
|
||||
static inline size_t seq_buf_strlen(struct seq_buf *s)
|
||||
{
|
||||
if (s->size == 0)
|
||||
return 0;
|
||||
|
||||
return __seq_buf_terminate(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_terminate - NUL-terminate the string in a seq_buf
|
||||
* @s: the seq_buf handle
|
||||
*
|
||||
* Terminate @s->buffer exactly as seq_buf_str() and seq_buf_strlen() do,
|
||||
* for callers that want neither the pointer nor the length and only need
|
||||
* the buffer to be safe to read as a C string. A zero-sized seq_buf has
|
||||
* nowhere to put a NUL and is left untouched.
|
||||
*
|
||||
* Nothing is returned on purpose: a caller that wants the length should
|
||||
* use seq_buf_strlen(), which says so.
|
||||
*
|
||||
* After this function is called, s->buffer is safe to use
|
||||
* in string operations.
|
||||
*/
|
||||
static inline void seq_buf_terminate(struct seq_buf *s)
|
||||
{
|
||||
if (s->size == 0)
|
||||
return;
|
||||
|
||||
__seq_buf_terminate(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* seq_buf_get_buf - get buffer to write arbitrary data to
|
||||
* @s: the seq_buf handle
|
||||
|
|
@ -155,11 +280,11 @@ static inline void seq_buf_commit(struct seq_buf *s, int num)
|
|||
*
|
||||
* Removes the last written character to the seq_buf @s.
|
||||
*
|
||||
* Returns the last character or -1 if it is empty.
|
||||
* Returns the last character, or -1 if @s is empty or has overflowed.
|
||||
*/
|
||||
static inline int seq_buf_pop(struct seq_buf *s)
|
||||
{
|
||||
if (!s->len)
|
||||
if (!s->len || seq_buf_has_overflowed(s))
|
||||
return -1;
|
||||
|
||||
s->len--;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,10 @@ static inline bool trace_seq_has_overflowed(struct trace_seq *s)
|
|||
*
|
||||
* Removes the last written character to the trace_seq @s.
|
||||
*
|
||||
* Returns the last character or -1 if it is empty.
|
||||
* Returns the last character, or -1 if the underlying seq_buf is empty or
|
||||
* has overflowed. Note that only that buffer is consulted: a @s marked
|
||||
* full by a write that did not fit, which trace_seq_has_overflowed()
|
||||
* reports as overflowed, still pops the last character written.
|
||||
*/
|
||||
static inline int trace_seq_pop(struct trace_seq *s)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ static void diag_fmt_restore(struct bpf_verifier_env *env, struct diag_fmt_mark
|
|||
|
||||
if (mark.chunk) {
|
||||
mark.chunk->seq.len = mark.len;
|
||||
seq_buf_str(&mark.chunk->seq);
|
||||
seq_buf_terminate(&mark.chunk->seq);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -631,11 +631,7 @@ static void format_disasm_line(struct bpf_verifier_env *env, int insn_idx,
|
|||
return;
|
||||
|
||||
print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
|
||||
seq_buf_str(&ctx.seq);
|
||||
ctx.seq.len = strnlen(line->text, sizeof(line->text));
|
||||
while (ctx.seq.len && line->text[ctx.seq.len - 1] == '\n')
|
||||
seq_buf_pop(&ctx.seq);
|
||||
seq_buf_str(&ctx.seq);
|
||||
seq_buf_terminate(&ctx.seq);
|
||||
|
||||
line->valid = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4908,7 +4908,7 @@ static __init int event_trace_enable(void)
|
|||
*/
|
||||
__trace_early_add_events(tr);
|
||||
|
||||
seq_buf_str(&bootup_event_seq);
|
||||
seq_buf_terminate(&bootup_event_seq);
|
||||
early_enable_events(tr, bootup_event_buf, false);
|
||||
|
||||
trace_printk_start_comm();
|
||||
|
|
@ -4937,7 +4937,7 @@ static __init int event_trace_enable_again(void)
|
|||
if (!tr)
|
||||
return -ENODEV;
|
||||
|
||||
seq_buf_str(&bootup_event_seq);
|
||||
seq_buf_terminate(&bootup_event_seq);
|
||||
early_enable_events(tr, bootup_event_buf, true);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -2985,8 +2985,7 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data,
|
|||
seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL);
|
||||
seq_buf_printf(&s, "synthetic_%s", field_name);
|
||||
|
||||
/* Terminate synthetic_name with a NUL. */
|
||||
seq_buf_str(&s);
|
||||
seq_buf_terminate(&s);
|
||||
|
||||
if (seq_buf_has_overflowed(&s)) {
|
||||
kfree(synthetic_name);
|
||||
|
|
@ -3103,8 +3102,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
|
|||
if (saved_filter)
|
||||
seq_buf_printf(&s, " if %s", saved_filter);
|
||||
|
||||
/* Terminate cmd with a NUL. */
|
||||
seq_buf_str(&s);
|
||||
seq_buf_terminate(&s);
|
||||
|
||||
if (seq_buf_has_overflowed(&s)) {
|
||||
kfree(cmd);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
|
|||
s->len += len;
|
||||
return 0;
|
||||
}
|
||||
/* vsnprintf() wrote as much as fits, so none of it is stale */
|
||||
s->len = s->size;
|
||||
}
|
||||
seq_buf_set_overflow(s);
|
||||
return -1;
|
||||
|
|
@ -128,7 +130,7 @@ void seq_buf_do_printk(struct seq_buf *s, const char *lvl)
|
|||
}
|
||||
|
||||
/* No trailing LF */
|
||||
if (start < s->buffer + s->len)
|
||||
if (*start)
|
||||
printk("%s%s\n", lvl, start);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(seq_buf_do_printk);
|
||||
|
|
@ -164,6 +166,8 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
|
|||
s->len += ret;
|
||||
return 0;
|
||||
}
|
||||
/* bstr_printf() wrote as much as fits, so none of it is stale */
|
||||
s->len = s->size;
|
||||
}
|
||||
seq_buf_set_overflow(s);
|
||||
return -1;
|
||||
|
|
@ -175,7 +179,9 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
|
|||
* @s: seq_buf descriptor
|
||||
* @str: simple string to record
|
||||
*
|
||||
* Copy a simple string into the sequence buffer.
|
||||
* Copy a simple string into the sequence buffer. If @str does not fit,
|
||||
* as much of it as fits is copied, followed by a null byte, as
|
||||
* seq_buf_printf() does.
|
||||
*
|
||||
* Returns: zero on success, -1 on overflow.
|
||||
*/
|
||||
|
|
@ -194,6 +200,11 @@ int seq_buf_puts(struct seq_buf *s, const char *str)
|
|||
s->len += len - 1;
|
||||
return 0;
|
||||
}
|
||||
/* Copy what fits, so the buffer never holds stale bytes */
|
||||
if (s->len < s->size) {
|
||||
strscpy(s->buffer + s->len, str, s->size - s->len);
|
||||
s->len = s->size;
|
||||
}
|
||||
seq_buf_set_overflow(s);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -229,7 +240,7 @@ EXPORT_SYMBOL_GPL(seq_buf_putc);
|
|||
*
|
||||
* There may be cases where raw memory needs to be written into the
|
||||
* buffer and a strcpy() would not work. Using this function allows
|
||||
* for such cases.
|
||||
* for such cases. If @mem does not fit, as much of it as fits is copied.
|
||||
*
|
||||
* Returns: zero on success, -1 on overflow.
|
||||
*/
|
||||
|
|
@ -242,9 +253,15 @@ int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
|
|||
s->len += len;
|
||||
return 0;
|
||||
}
|
||||
/* Copy what fits, so the buffer never holds stale bytes */
|
||||
if (s->len < s->size) {
|
||||
memcpy(s->buffer + s->len, mem, s->size - s->len);
|
||||
s->len = s->size;
|
||||
}
|
||||
seq_buf_set_overflow(s);
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(seq_buf_putmem);
|
||||
|
||||
#define MAX_MEMHEX_BYTES 8U
|
||||
#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1)
|
||||
|
|
@ -330,6 +347,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
|
|||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(seq_buf_path);
|
||||
|
||||
/**
|
||||
* seq_buf_to_user - copy the sequence buffer to user space
|
||||
|
|
@ -391,12 +409,13 @@ int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, size_t start, int cnt)
|
|||
*
|
||||
* Function is an analogue of print_hex_dump() and thus has similar interface.
|
||||
*
|
||||
* linebuf size is maximal length for one line.
|
||||
* 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
|
||||
* separating space
|
||||
* 2 - spaces separating hex dump and ASCII representation
|
||||
* 32 - ASCII representation
|
||||
* 1 - terminating '\0'
|
||||
* linebuf size is maximal length for one line::
|
||||
*
|
||||
* 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
|
||||
* separating space
|
||||
* 2 - spaces separating hex dump and ASCII representation
|
||||
* 32 - ASCII representation
|
||||
* 1 - terminating '\0'
|
||||
*
|
||||
* Returns: zero on success, -1 on overflow.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
static void seq_buf_init_test(struct kunit *test)
|
||||
{
|
||||
|
|
@ -21,6 +25,75 @@ static void seq_buf_init_test(struct kunit *test)
|
|||
KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 32);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 0);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
|
||||
}
|
||||
|
||||
static void seq_buf_init_append_test(struct kunit *test)
|
||||
{
|
||||
char buf[32] = "hello";
|
||||
struct seq_buf s;
|
||||
|
||||
/* Initial string contents match. */
|
||||
seq_buf_init_append(&s, buf, sizeof(buf));
|
||||
KUNIT_EXPECT_EQ(test, s.size, 32);
|
||||
KUNIT_EXPECT_EQ(test, s.len, 5);
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 32 - 5);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 5);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
|
||||
|
||||
/* Appending with space works. */
|
||||
seq_buf_puts(&s, " world");
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
|
||||
|
||||
/* No truncation when space for NUL is present. */
|
||||
seq_buf_init_append(&s, buf, 12);
|
||||
KUNIT_EXPECT_EQ(test, s.size, 12);
|
||||
KUNIT_EXPECT_EQ(test, s.len, 11);
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
|
||||
|
||||
/*
|
||||
* No NUL within the size: the content is already truncated, as
|
||||
* strlcat() would see it, so @s starts out overflowed. The content
|
||||
* stays, and appending adds nothing.
|
||||
*/
|
||||
seq_buf_init_append(&s, buf, 11);
|
||||
KUNIT_EXPECT_EQ(test, s.size, 11);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 0);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, "hello world", 11);
|
||||
seq_buf_puts(&s, "!");
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, "hello world", 11);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello worl");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 10);
|
||||
|
||||
/*
|
||||
* The size bounds the scan: the string runs past it, so there is
|
||||
* no NUL within the size either.
|
||||
*/
|
||||
seq_buf_init_append(&s, buf, 5);
|
||||
KUNIT_EXPECT_EQ(test, s.size, 5);
|
||||
/* Overflowed at the size; an unbounded scan would find more. */
|
||||
KUNIT_EXPECT_EQ(test, s.len, 5 + 1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 0);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hell");
|
||||
|
||||
/* With no room even for a NUL, @s is overflowed and @buf untouched. */
|
||||
seq_buf_init_append(&s, buf, 0);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 0);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
|
||||
KUNIT_EXPECT_EQ(test, buf[0], 'h');
|
||||
}
|
||||
|
||||
static void seq_buf_declare_test(struct kunit *test)
|
||||
|
|
@ -113,6 +186,47 @@ static void seq_buf_putc_test(struct kunit *test)
|
|||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
|
||||
}
|
||||
|
||||
static void seq_buf_pop_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 8);
|
||||
struct seq_buf t;
|
||||
char *buf;
|
||||
|
||||
/* Nothing to pop. */
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), -1);
|
||||
KUNIT_EXPECT_EQ(test, s.len, 0);
|
||||
|
||||
seq_buf_puts(&s, "hello");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 'o');
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 4);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hell");
|
||||
|
||||
/* A 0xff byte must not be mistaken for an empty buffer. */
|
||||
seq_buf_putc(&s, 0xff);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 0xff);
|
||||
|
||||
/* A full buffer pops its last byte. */
|
||||
seq_buf_puts(&s, "abc");
|
||||
seq_buf_putc(&s, 'd');
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 8);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 'd');
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 7);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hellabc");
|
||||
|
||||
/*
|
||||
* An overflowed buffer has nothing to pop, and stays overflowed. Use
|
||||
* a buffer allocated at its exact size, so that KASAN reports any
|
||||
* read past its end.
|
||||
*/
|
||||
buf = kunit_kmalloc(test, 16, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, buf);
|
||||
seq_buf_init(&t, buf, 16);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printf(&t, "%s", "longer than sixteen"), -1);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_pop(&t), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
|
||||
}
|
||||
|
||||
static void seq_buf_printf_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 32);
|
||||
|
|
@ -205,9 +319,9 @@ static void seq_buf_putmem_hex_overflow_test(struct kunit *test)
|
|||
DECLARE_SEQ_BUF(s, 20);
|
||||
const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
#ifdef __BIG_ENDIAN
|
||||
const char *expected = "0001020304050607 ";
|
||||
const char *expected = "0001020304050607 08";
|
||||
#else
|
||||
const char *expected = "0706050403020100 ";
|
||||
const char *expected = "0706050403020100 09";
|
||||
#endif
|
||||
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1);
|
||||
|
|
@ -216,18 +330,437 @@ static void seq_buf_putmem_hex_overflow_test(struct kunit *test)
|
|||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
|
||||
}
|
||||
|
||||
static void seq_buf_puts_partial_overflow_test(struct kunit *test)
|
||||
{
|
||||
static const char expected[] = "abcdefg";
|
||||
DECLARE_SEQ_BUF(s, 16);
|
||||
struct seq_buf t;
|
||||
char buf[8];
|
||||
|
||||
/* As much of the string as fits is written, like seq_buf_printf(). */
|
||||
seq_buf_puts(&s, "hello");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_puts(&s, " world, again"), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 16);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world, ag");
|
||||
|
||||
/* Stale bytes after the data must not show up in the string. */
|
||||
memset(buf, 'X', sizeof(buf));
|
||||
seq_buf_init(&t, buf, sizeof(buf));
|
||||
seq_buf_putc(&t, 'a');
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_puts(&t, "bcdefghij"), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
|
||||
|
||||
/*
|
||||
* Check the buffer before seq_buf_str() does: it would write the
|
||||
* terminator over the last byte itself, hiding whether the copy
|
||||
* placed one there. The literal's own NUL is the eighth byte.
|
||||
*/
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, expected, sizeof(buf));
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "abcdefg");
|
||||
}
|
||||
|
||||
static void seq_buf_putmem_partial_overflow_test(struct kunit *test)
|
||||
{
|
||||
const u8 data[] = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
const char expected[] = { 'a', 'b', 1, 2, 3, 4, 5, 6 };
|
||||
struct seq_buf s;
|
||||
char buf[8];
|
||||
|
||||
memset(buf, 'X', sizeof(buf));
|
||||
seq_buf_init(&s, buf, sizeof(buf));
|
||||
seq_buf_putmem(&s, "ab", 2);
|
||||
|
||||
/* One byte too many, so the last byte of @data is dropped. */
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_putmem(&s, data, sizeof(data)), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
|
||||
/*
|
||||
* Check the buffer rather than seq_buf_str(): seq_buf_putmem() writes
|
||||
* no NUL of its own, so a short copy leaves a stale byte at the end,
|
||||
* exactly where seq_buf_str() would then write the terminator and
|
||||
* hide it.
|
||||
*/
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, expected, sizeof(buf));
|
||||
}
|
||||
|
||||
static void seq_buf_putmem_hex_partial_overflow_test(struct kunit *test)
|
||||
{
|
||||
const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
#ifdef __BIG_ENDIAN
|
||||
const char *expected = "0001020304050607 08";
|
||||
static const char expected_raw[] = "0001020304050607 080";
|
||||
#else
|
||||
const char *expected = "0706050403020100 09";
|
||||
static const char expected_raw[] = "0706050403020100 090";
|
||||
#endif
|
||||
struct seq_buf s;
|
||||
char buf[20];
|
||||
|
||||
/* Stale bytes after the data must not show up in the string. */
|
||||
memset(buf, 'X', sizeof(buf));
|
||||
seq_buf_init(&s, buf, sizeof(buf));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
|
||||
/* Before seq_buf_str() writes the terminator over the last byte. */
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, expected_raw, sizeof(buf));
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
|
||||
}
|
||||
|
||||
/*
|
||||
* Counters for the console that seq_buf_do_printk_test() registers while it
|
||||
* runs. Only records carrying the marker are counted, so unrelated kernel
|
||||
* messages do not disturb them.
|
||||
*
|
||||
* An empty record carries nothing to recognize it by, so count one only
|
||||
* where the flaw puts it: directly after a record of ours, with nothing in
|
||||
* between. That still misreads a bare line feed printed by another CPU in
|
||||
* exactly that gap, but no longer counts one printed at any point while the
|
||||
* console happens to be registered.
|
||||
*/
|
||||
#define SEQ_BUF_PRINTK_MARKER "sbdpkx"
|
||||
|
||||
static unsigned int seq_buf_printk_marked;
|
||||
static unsigned int seq_buf_printk_empty;
|
||||
static bool seq_buf_printk_last_was_ours;
|
||||
|
||||
static void seq_buf_printk_capture(struct console *con,
|
||||
const char *s, unsigned int count)
|
||||
{
|
||||
const char *text = s;
|
||||
const char *prefix;
|
||||
|
||||
/*
|
||||
* Skip what printk() puts in front of the message: a timestamp,
|
||||
* and the caller id as well under CONFIG_PRINTK_CALLER, so strip
|
||||
* every bracketed group rather than just the first.
|
||||
*/
|
||||
while (count && text[0] == '[') {
|
||||
prefix = memchr(text, ']', count);
|
||||
if (!prefix)
|
||||
break;
|
||||
count -= prefix + 1 - text;
|
||||
text = prefix + 1;
|
||||
if (count && text[0] == ' ') {
|
||||
text++;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (strnstr(text, SEQ_BUF_PRINTK_MARKER, count)) {
|
||||
seq_buf_printk_marked++;
|
||||
seq_buf_printk_last_was_ours = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (seq_buf_printk_last_was_ours &&
|
||||
(count == 0 || (count == 1 && text[0] == '\n')))
|
||||
seq_buf_printk_empty++;
|
||||
|
||||
seq_buf_printk_last_was_ours = false;
|
||||
}
|
||||
|
||||
static void seq_buf_printk_run(struct console *capture, struct seq_buf *s)
|
||||
{
|
||||
seq_buf_printk_marked = 0;
|
||||
seq_buf_printk_empty = 0;
|
||||
seq_buf_printk_last_was_ours = false;
|
||||
|
||||
/*
|
||||
* register_console() will not take an unmatched console without
|
||||
* CON_ENABLED, and unregister_console() clears it, so set it on
|
||||
* every run to keep the test repeatable.
|
||||
*/
|
||||
capture->flags = CON_ENABLED;
|
||||
register_console(capture);
|
||||
seq_buf_do_printk(s, KERN_INFO);
|
||||
unregister_console(capture);
|
||||
}
|
||||
|
||||
static void seq_buf_do_printk_test(struct kunit *test)
|
||||
{
|
||||
/*
|
||||
* A registered console is a global object: printk() reaches it
|
||||
* through the console list from any CPU, and the console code writes
|
||||
* back into it, so keep it out of this function's stack frame the
|
||||
* way every other console in the tree does.
|
||||
*/
|
||||
static struct console capture = {
|
||||
.name = "sbufcap",
|
||||
.write = seq_buf_printk_capture,
|
||||
.index = -1,
|
||||
};
|
||||
DECLARE_SEQ_BUF(s, 8);
|
||||
DECLARE_SEQ_BUF(t, 16);
|
||||
DECLARE_SEQ_BUF(u, 8);
|
||||
|
||||
/*
|
||||
* Fill the buffer exactly, so that the NUL takes the place of the
|
||||
* last byte and the string ends with the line feed before it.
|
||||
*/
|
||||
seq_buf_puts(&s, SEQ_BUF_PRINTK_MARKER);
|
||||
seq_buf_putc(&s, '\n');
|
||||
seq_buf_putc(&s, '!');
|
||||
KUNIT_ASSERT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_ASSERT_EQ(test, seq_buf_used(&s), 8);
|
||||
KUNIT_ASSERT_EQ(test, strlen(seq_buf_str(&s)), 7);
|
||||
|
||||
seq_buf_printk_run(&capture, &s);
|
||||
|
||||
/* The one line that was written, and nothing after it. */
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
|
||||
|
||||
/* Check that lines without a trailing newline are shown. */
|
||||
seq_buf_puts(&t, SEQ_BUF_PRINTK_MARKER "\n" SEQ_BUF_PRINTK_MARKER);
|
||||
KUNIT_ASSERT_FALSE(test, seq_buf_has_overflowed(&t));
|
||||
|
||||
seq_buf_printk_run(&capture, &t);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 2);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
|
||||
|
||||
/*
|
||||
* The buffer above was exactly full, where "len" equals the size. A
|
||||
* buffer that actually overflowed reaches the same bug by the other
|
||||
* route the old test had, with "len" one past the size.
|
||||
*/
|
||||
seq_buf_puts(&u, SEQ_BUF_PRINTK_MARKER "\n");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_puts(&u, "yy"), -1);
|
||||
KUNIT_ASSERT_TRUE(test, seq_buf_has_overflowed(&u));
|
||||
KUNIT_ASSERT_EQ(test, u.len, u.size + 1);
|
||||
|
||||
seq_buf_printk_run(&capture, &u);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
|
||||
}
|
||||
|
||||
/* Long enough that it cannot fit in the room the test leaves for it. */
|
||||
#define SEQ_BUF_TEST_PATH "/seq_buf_kunit_path_name"
|
||||
|
||||
static void seq_buf_path_overflow_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 32);
|
||||
const char *expected = "keep:xxxxxxxxxxxxxxxxxxxxxxx";
|
||||
struct file *file;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The tests run before anything writable is mounted, so take the file
|
||||
* whose path gets printed from shmem, which needs no mount of its own.
|
||||
*/
|
||||
file = shmem_file_setup(SEQ_BUF_TEST_PATH, 0, EMPTY_VMA_FLAGS);
|
||||
if (IS_ERR(file))
|
||||
kunit_skip(test, "cannot create a file to print the path of");
|
||||
|
||||
/* Leave less room than the path needs, so d_path() cannot fit it. */
|
||||
seq_buf_puts(&s, "keep:");
|
||||
len = seq_buf_used(&s);
|
||||
for (i = len; i < 28; i++)
|
||||
seq_buf_putc(&s, 'x');
|
||||
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_path(&s, &file->f_path, "\n"), -1);
|
||||
fput(file);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
|
||||
/*
|
||||
* d_path() keeps as much of the path as fits when it does not fit
|
||||
* whole, and seq_buf_str() would hand out that fragment, as it ends
|
||||
* the string at the last byte of an overflowed buffer.
|
||||
*/
|
||||
for (i = 28; i < 32; i++)
|
||||
KUNIT_EXPECT_EQ_MSG(test, s.buffer[i], '\0',
|
||||
"byte %d past the data is not cleared", i);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 16);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
|
||||
|
||||
seq_buf_puts(&s, "hello");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
|
||||
seq_buf_printf(&s, " %s", "world");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_printf_overflow_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 16);
|
||||
DECLARE_SEQ_BUF(t, 8);
|
||||
|
||||
seq_buf_printf(&s, "%s", "1234567890abcdefghij");
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 16);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 15);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "1234567890abcde");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
|
||||
/* Output one byte too long for the NUL. */
|
||||
seq_buf_printf(&t, "%s", "12345678");
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&t), 8);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), 7);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "1234567");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), strlen(seq_buf_str(&t)));
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_full_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 4);
|
||||
DECLARE_SEQ_BUF(t, 8);
|
||||
char *buf;
|
||||
size_t len;
|
||||
|
||||
/* Filled exactly, with no room left for a NUL, but not overflowed. */
|
||||
seq_buf_putc(&s, 'a');
|
||||
seq_buf_putc(&s, 'b');
|
||||
seq_buf_putc(&s, 'c');
|
||||
seq_buf_putc(&s, 'd');
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 4);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 3);
|
||||
/* seq_buf_strlen() terminates the buffer by itself. */
|
||||
KUNIT_EXPECT_EQ(test, s.buffer[3], '\0');
|
||||
KUNIT_EXPECT_EQ(test, strnlen(s.buffer, s.size), 3);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "abc");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
|
||||
/* A printf into a full buffer writes nothing. */
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_printf(&s, "%s", "x"), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 3);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "abc");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
|
||||
len = seq_buf_get_buf(&t, &buf);
|
||||
KUNIT_ASSERT_EQ(test, len, 8);
|
||||
memset(buf, 'z', len);
|
||||
seq_buf_commit(&t, len);
|
||||
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&t));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), 7);
|
||||
KUNIT_EXPECT_EQ(test, t.buffer[7], '\0');
|
||||
KUNIT_EXPECT_EQ(test, strnlen(t.buffer, t.size), 7);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "zzzzzzz");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), strlen(seq_buf_str(&t)));
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_puts_overflow_test(struct kunit *test)
|
||||
{
|
||||
DECLARE_SEQ_BUF(s, 16);
|
||||
|
||||
/* A puts that does not fit copies as much as fits. */
|
||||
seq_buf_puts(&s, "hello");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_puts(&s, " this does not fit"), -1);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 15);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello this does");
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_embedded_nul_test(struct kunit *test)
|
||||
{
|
||||
static const char data[] = "ab\0cd";
|
||||
DECLARE_SEQ_BUF(s, 16);
|
||||
|
||||
/*
|
||||
* seq_buf_strlen() reports where it put the terminator, not where
|
||||
* the first NUL is, so data carrying a NUL of its own makes the two
|
||||
* disagree. That is expected, and is what the documented caveat is
|
||||
* about.
|
||||
*/
|
||||
seq_buf_putmem(&s, data, sizeof(data) - 1);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
|
||||
KUNIT_EXPECT_EQ(test, strlen(seq_buf_str(&s)), 2);
|
||||
}
|
||||
|
||||
static void seq_buf_strlen_zero_size_test(struct kunit *test)
|
||||
{
|
||||
char buf[] = "untouched";
|
||||
struct seq_buf s;
|
||||
|
||||
/*
|
||||
* A zero-sized seq_buf has nowhere to put a terminator. Both
|
||||
* accessors report an empty string and leave the buffer alone
|
||||
* rather than writing outside it.
|
||||
*/
|
||||
seq_buf_init(&s, buf, 0);
|
||||
KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
|
||||
KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
|
||||
KUNIT_EXPECT_STREQ(test, buf, "untouched");
|
||||
}
|
||||
|
||||
static void seq_buf_terminate_test(struct kunit *test)
|
||||
{
|
||||
char buf[16];
|
||||
struct seq_buf s;
|
||||
|
||||
/*
|
||||
* seq_buf_putmem() writes no NUL, so only seq_buf_terminate() can
|
||||
* end these strings early. The NUL in the last byte of buf keeps
|
||||
* the comparisons in bounds if it does not.
|
||||
*/
|
||||
|
||||
/* Terminates directly after the data when there is room. */
|
||||
memset(buf, 'z', sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
seq_buf_init(&s, buf, sizeof(buf));
|
||||
seq_buf_putmem(&s, "ab", 2);
|
||||
seq_buf_terminate(&s);
|
||||
KUNIT_EXPECT_STREQ(test, buf, "ab");
|
||||
|
||||
/* Terminates in the last byte once the buffer has overflowed. */
|
||||
memset(buf, 'z', sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
seq_buf_init(&s, buf, 4);
|
||||
seq_buf_putmem(&s, "abcdef", 6);
|
||||
KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
|
||||
seq_buf_terminate(&s);
|
||||
KUNIT_EXPECT_STREQ(test, buf, "abc");
|
||||
|
||||
/* A zero-sized seq_buf is left alone. */
|
||||
strscpy(buf, "untouched", sizeof(buf));
|
||||
seq_buf_init(&s, buf, 0);
|
||||
seq_buf_terminate(&s);
|
||||
KUNIT_EXPECT_STREQ(test, buf, "untouched");
|
||||
}
|
||||
|
||||
static struct kunit_case seq_buf_test_cases[] = {
|
||||
KUNIT_CASE(seq_buf_init_test),
|
||||
KUNIT_CASE(seq_buf_init_append_test),
|
||||
KUNIT_CASE(seq_buf_declare_test),
|
||||
KUNIT_CASE(seq_buf_clear_test),
|
||||
KUNIT_CASE(seq_buf_puts_test),
|
||||
KUNIT_CASE(seq_buf_puts_overflow_test),
|
||||
KUNIT_CASE(seq_buf_putc_test),
|
||||
KUNIT_CASE(seq_buf_pop_test),
|
||||
KUNIT_CASE(seq_buf_printf_test),
|
||||
KUNIT_CASE(seq_buf_printf_overflow_test),
|
||||
KUNIT_CASE(seq_buf_get_buf_commit_test),
|
||||
KUNIT_CASE(seq_buf_putmem_hex_test),
|
||||
KUNIT_CASE(seq_buf_putmem_hex_overflow_test),
|
||||
KUNIT_CASE(seq_buf_puts_partial_overflow_test),
|
||||
KUNIT_CASE(seq_buf_putmem_partial_overflow_test),
|
||||
KUNIT_CASE(seq_buf_putmem_hex_partial_overflow_test),
|
||||
KUNIT_CASE(seq_buf_path_overflow_test),
|
||||
KUNIT_CASE(seq_buf_strlen_test),
|
||||
KUNIT_CASE(seq_buf_strlen_printf_overflow_test),
|
||||
KUNIT_CASE(seq_buf_strlen_full_test),
|
||||
KUNIT_CASE(seq_buf_strlen_puts_overflow_test),
|
||||
KUNIT_CASE(seq_buf_strlen_embedded_nul_test),
|
||||
KUNIT_CASE(seq_buf_strlen_zero_size_test),
|
||||
KUNIT_CASE(seq_buf_terminate_test),
|
||||
KUNIT_CASE(seq_buf_do_printk_test),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -369,17 +369,24 @@ union test_small_end {
|
|||
char four;
|
||||
};
|
||||
|
||||
#define ALWAYS_PASS WANT_SUCCESS
|
||||
#define ALWAYS_FAIL XFAIL
|
||||
#define ALWAYS_PASS WANT_SUCCESS
|
||||
#define ALWAYS_FAIL XFAIL
|
||||
|
||||
#ifdef CONFIG_INIT_STACK_NONE
|
||||
# define USER_PASS XFAIL
|
||||
# define BYREF_PASS XFAIL
|
||||
# define STRONG_PASS XFAIL
|
||||
# define INSTRUMENTED_PASS XFAIL
|
||||
#else
|
||||
# define USER_PASS WANT_SUCCESS
|
||||
# define BYREF_PASS WANT_SUCCESS
|
||||
# define STRONG_PASS WANT_SUCCESS
|
||||
# define INSTRUMENTED_PASS WANT_SUCCESS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clang 24 and later emit initialization on the jumps that bypass the
|
||||
* declaration. GCC still initializes only at the declaration. Track
|
||||
* test expectation based on CONFIG_INIT_STACK_NONE above.
|
||||
*/
|
||||
#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 240000
|
||||
# define BYPASS_PASS INSTRUMENTED_PASS
|
||||
#else
|
||||
# define BYPASS_PASS XFAIL
|
||||
#endif
|
||||
|
||||
#define DEFINE_SCALAR_TEST(name, init, xfail) \
|
||||
|
|
@ -432,23 +439,23 @@ DEFINE_STRUCT_TESTS(old_zero, ALWAYS_PASS);
|
|||
DEFINE_UNION_TESTS(zero, ALWAYS_PASS);
|
||||
DEFINE_UNION_TESTS(old_zero, ALWAYS_PASS);
|
||||
/* Struct initializers: padding may be left uninitialized. */
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(static, STRONG_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(dynamic, STRONG_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(runtime, STRONG_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(static, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(dynamic, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(runtime, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_TESTS(assigned_copy, ALWAYS_FAIL);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(static, STRONG_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(dynamic, STRONG_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(runtime, STRONG_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(static, INSTRUMENTED_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(dynamic, INSTRUMENTED_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(runtime, INSTRUMENTED_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(assigned_static, INSTRUMENTED_PASS);
|
||||
DEFINE_UNION_INITIALIZER_TESTS(assigned_dynamic, INSTRUMENTED_PASS);
|
||||
DEFINE_UNION_TESTS(assigned_copy, ALWAYS_FAIL);
|
||||
/* No initialization without compiler instrumentation. */
|
||||
DEFINE_SCALAR_TESTS(none, STRONG_PASS);
|
||||
DEFINE_STRUCT_TESTS(none, BYREF_PASS);
|
||||
DEFINE_SCALAR_TESTS(none, INSTRUMENTED_PASS);
|
||||
DEFINE_STRUCT_TESTS(none, INSTRUMENTED_PASS);
|
||||
/* Initialization of members with __user attribute. */
|
||||
DEFINE_TEST(user, struct test_user, STRUCT, none, USER_PASS);
|
||||
DEFINE_TEST(user, struct test_user, STRUCT, none, INSTRUMENTED_PASS);
|
||||
|
||||
/*
|
||||
* Check two uses through a variable declaration outside either path,
|
||||
|
|
@ -493,6 +500,36 @@ static int noinline __leaf_switch_none(int path, bool fill)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static noinline int __leaf_goto_none(bool fill)
|
||||
{
|
||||
goto bypass;
|
||||
/*
|
||||
* This declaration is jumped over by the goto above, so it is
|
||||
* never reached. Compilers that initialize only at the point of
|
||||
* declaration leave this variable untouched.
|
||||
*/
|
||||
uint64_t var[10];
|
||||
|
||||
bypass:
|
||||
target_start = &var;
|
||||
target_size = sizeof(var);
|
||||
if (fill) {
|
||||
fill_start = &var;
|
||||
fill_size = sizeof(var);
|
||||
|
||||
memset(fill_start, (forced_mask | 0x55) & FILL_BYTE, fill_size);
|
||||
}
|
||||
memcpy(check_buf, target_start, target_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static noinline int leaf_goto_none(unsigned long sp, bool fill,
|
||||
uint64_t *arg)
|
||||
{
|
||||
return __leaf_goto_none(fill);
|
||||
}
|
||||
|
||||
static noinline int leaf_switch_1_none(unsigned long sp, bool fill,
|
||||
uint64_t *arg)
|
||||
{
|
||||
|
|
@ -506,13 +543,14 @@ static noinline int leaf_switch_2_none(unsigned long sp, bool fill,
|
|||
}
|
||||
|
||||
/*
|
||||
* These are expected to fail for most configurations because neither
|
||||
* GCC nor Clang have a way to perform initialization of variables in
|
||||
* non-code areas (i.e. in a switch statement before the first "case").
|
||||
* https://llvm.org/pr44916
|
||||
* A declaration whose definition point is jumped over, either by a switch
|
||||
* dispatch reaching a "case" label below it or by a goto, used to be left
|
||||
* uninitialized: the initialization is emitted where the variable is
|
||||
* declared, and that point is never reached.
|
||||
*/
|
||||
DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, ALWAYS_FAIL);
|
||||
DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL);
|
||||
DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, BYPASS_PASS);
|
||||
DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, BYPASS_PASS);
|
||||
DEFINE_TEST_DRIVER(goto_none, uint64_t, SCALAR, BYPASS_PASS);
|
||||
|
||||
#define KUNIT_test_scalars(init) \
|
||||
KUNIT_CASE(test_u8_ ## init), \
|
||||
|
|
@ -568,6 +606,7 @@ static struct kunit_case stackinit_test_cases[] = {
|
|||
KUNIT_test_scalars(none),
|
||||
KUNIT_CASE(test_switch_1_none),
|
||||
KUNIT_CASE(test_switch_2_none),
|
||||
KUNIT_CASE(test_goto_none),
|
||||
/* STRUCTLEAK_BYREF should cover from here down. */
|
||||
KUNIT_test_structs(none),
|
||||
/* STRUCTLEAK will only cover this. */
|
||||
|
|
|
|||
|
|
@ -24,27 +24,57 @@ def alloc_array(name):
|
|||
print(f"Unknown transform for {name}", file=sys.stderr)
|
||||
return func
|
||||
|
||||
// This excludes anything that is assigning to or from integral types or
|
||||
// string literals. Everything else gets the sizeof() extracted for the
|
||||
// kmalloc_obj() type/var argument. sizeof(void *) is also excluded because
|
||||
// it will need case-by-case double-checking to make sure the right type is
|
||||
// Allocations sized by a byte-sized type (BYTE_TYPES) or a string literal,
|
||||
// and allocations assigned to a pointer to a byte-sized type, are byte
|
||||
// buffers and are left alone. sizeof(void *) is also excluded because it
|
||||
// will need case-by-case double-checking to make sure the right type is
|
||||
// being assigned.
|
||||
//
|
||||
// Allocations sized by a multi-byte integral type (MULTIBYTE_TYPES) are
|
||||
// converted when they are assigned to a pointer to that same type.
|
||||
// Otherwise they are left alone: the target may be a pointer to an array
|
||||
// of that type, such as "s16 (*pairs)[2]", for which the converted
|
||||
// allocation would have the wrong pointer type. For the same reason,
|
||||
// arrays of pointers to integral types sized as sizeof(char *) and the
|
||||
// like are left alone. Allocations of other types assigned to a pointer to
|
||||
// a multi-byte integral type are left alone too.
|
||||
//
|
||||
// Everything else gets the sizeof() extracted for the kmalloc_obj()
|
||||
// type/var argument.
|
||||
//
|
||||
// The first matching alternative below wins, so the exclusions must come
|
||||
// before the more general conversions.
|
||||
@direct depends on patch && !(file in "tools") && !(file in "samples")@
|
||||
typedef u8, u16, u32, u64;
|
||||
typedef __u8, __u16, __u32, __u64;
|
||||
typedef uint8_t, uint16_t, uint32_t, uint64_t;
|
||||
typedef s8, s16, s32, s64;
|
||||
typedef __s8, __s16, __s32, __s64;
|
||||
typedef int8_t, int16_t, int32_t, int64_t;
|
||||
typedef uchar, ushort, uint, ulong;
|
||||
typedef __le16, __le32, __le64;
|
||||
typedef __be16, __be32, __be64;
|
||||
typedef wchar_t;
|
||||
type INTEGRAL = {u8,__u8,uint8_t,char,unsigned char,uchar,wchar_t,
|
||||
u16,__u16,uint16_t,unsigned short,ushort,
|
||||
u32,__u32,uint32_t,unsigned int,uint,
|
||||
u64,__u64,uint64_t,unsigned long,ulong,
|
||||
__le16,__le32,__le64,__be16,__be32,__be64};
|
||||
type BYTE_TYPES = {char,signed char,unsigned char,uchar,
|
||||
u8,__u8,uint8_t,s8,__s8,int8_t};
|
||||
type MULTIBYTE_TYPES = {short,short int,signed short,signed short int,
|
||||
unsigned short,unsigned short int,ushort,
|
||||
int,signed,signed int,unsigned,unsigned int,uint,
|
||||
long,long int,signed long,signed long int,
|
||||
unsigned long,unsigned long int,ulong,
|
||||
long long,long long int,
|
||||
signed long long,signed long long int,
|
||||
unsigned long long,unsigned long long int,
|
||||
u16,__u16,uint16_t,s16,__s16,int16_t,
|
||||
u32,__u32,uint32_t,s32,__s32,int32_t,
|
||||
u64,__u64,uint64_t,s64,__s64,int64_t,
|
||||
__le16,__le32,__le64,__be16,__be32,__be64,
|
||||
wchar_t};
|
||||
char [] STRING;
|
||||
INTEGRAL *BYTES;
|
||||
INTEGRAL **BYTES_PTRS;
|
||||
BYTE_TYPES *BYTES;
|
||||
MULTIBYTE_TYPES *MULTIBYTES;
|
||||
const MULTIBYTE_TYPES *CONST_MULTIBYTES;
|
||||
MULTIBYTE_TYPES MULTIBYTE;
|
||||
type TYPE;
|
||||
expression VAR;
|
||||
expression GFP;
|
||||
|
|
@ -59,66 +89,133 @@ fresh identifier ALLOC_OBJS = script:python(ALLOC_ARRAY) { alloc_array(ALLOC_ARR
|
|||
@@
|
||||
|
||||
(
|
||||
// Convert a single object sized by its target: p = kmalloc(sizeof(*p), gfp)
|
||||
- VAR = ALLOC((sizeof(*VAR)), GFP)
|
||||
+ VAR = ALLOC_OBJ(*VAR, GFP)
|
||||
|
|
||||
ALLOC((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP)
|
||||
// Exclude byte buffers and integral pointers: kmalloc(sizeof(u8), gfp),
|
||||
// kmalloc(sizeof("str"), gfp), kmalloc(sizeof(char *), gfp)
|
||||
ALLOC((\(sizeof(STRING)\|sizeof(BYTE_TYPES)\|
|
||||
sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), GFP)
|
||||
|
|
||||
BYTES = ALLOC((sizeof(E)), GFP)
|
||||
// Exclude anything assigned to a byte pointer:
|
||||
// u8 *buf = kmalloc(sizeof(*hdr), gfp)
|
||||
BYTES = ALLOC((\(sizeof(E)\|sizeof(TYPE)\)), GFP)
|
||||
|
|
||||
BYTES = ALLOC((sizeof(TYPE)), GFP)
|
||||
// Convert a multi-byte type to a pointer to it:
|
||||
// u32 *p = kmalloc(sizeof(u32), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC((sizeof(MULTIBYTE_TYPES)), GFP)
|
||||
+ ALLOC_OBJ(MULTIBYTE_TYPES, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC((sizeof(E)), GFP)
|
||||
// Same by expression: u32 *p = kmalloc(sizeof(p[0]), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC((sizeof(MULTIBYTE)), GFP)
|
||||
+ ALLOC_OBJ(MULTIBYTE, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC((sizeof(TYPE)), GFP)
|
||||
// Exclude other multi-byte sizes, e.g. to pointers to arrays: s16 (*p)[2] = ...
|
||||
ALLOC((\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), GFP)
|
||||
|
|
||||
// Exclude anything else assigned to a multi-byte pointer:
|
||||
// u32 *p = kmalloc(sizeof(*hdr), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC((\(sizeof(E)\|sizeof(TYPE)\)), GFP)
|
||||
|
|
||||
// Exclude void pointers, to be checked by hand: kmalloc(sizeof(void *), gfp)
|
||||
ALLOC((sizeof(void *)), GFP)
|
||||
|
|
||||
// Convert any other expression: p = kmalloc(sizeof(s->item), gfp)
|
||||
- ALLOC((sizeof(E)), GFP)
|
||||
+ ALLOC_OBJ(E, GFP)
|
||||
|
|
||||
// Convert any other type: p = kmalloc(sizeof(struct item), gfp)
|
||||
- ALLOC((sizeof(TYPE)), GFP)
|
||||
+ ALLOC_OBJ(TYPE, GFP)
|
||||
|
|
||||
ALLOC_ARRAY(COUNT, (\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP)
|
||||
// The same, for arrays allocated as (count, size):
|
||||
// Exclude byte buffers and integral pointers: kcalloc(n, sizeof(u8), gfp),
|
||||
// kcalloc(n, sizeof(char *), gfp)
|
||||
ALLOC_ARRAY(COUNT, (\(sizeof(STRING)\|sizeof(BYTE_TYPES)\|
|
||||
sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), GFP)
|
||||
|
|
||||
BYTES = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
|
||||
// Exclude arrays assigned to a byte pointer:
|
||||
// u8 *buf = kcalloc(n, sizeof(*hdr), gfp)
|
||||
BYTES = ALLOC_ARRAY(COUNT, (\(sizeof(E)\|sizeof(TYPE)\)), GFP)
|
||||
|
|
||||
BYTES = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
|
||||
// Convert a multi-byte array to a pointer to it:
|
||||
// u32 *p = kcalloc(n, sizeof(u32), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC_ARRAY(COUNT, (sizeof(MULTIBYTE_TYPES)), GFP)
|
||||
+ ALLOC_OBJS(MULTIBYTE_TYPES, COUNT, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
|
||||
// Same by expression: u32 *p = kcalloc(n, sizeof(*p), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC_ARRAY(COUNT, (sizeof(MULTIBYTE)), GFP)
|
||||
+ ALLOC_OBJS(MULTIBYTE, COUNT, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
|
||||
// Exclude other multi-byte arrays:
|
||||
// s16 (*pairs)[2] = kcalloc(n, sizeof(s16), gfp)
|
||||
ALLOC_ARRAY(COUNT, (\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), GFP)
|
||||
|
|
||||
ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), COUNT, GFP)
|
||||
|
|
||||
BYTES = ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
|
||||
|
|
||||
BYTES = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
|
||||
|
|
||||
BYTES_PTRS = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
|
||||
// Exclude other arrays assigned to a multi-byte pointer:
|
||||
// u32 *p = kcalloc(n, sizeof(*hdr), gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC_ARRAY(COUNT, (\(sizeof(E)\|sizeof(TYPE)\)), GFP)
|
||||
|
|
||||
// Exclude arrays of void pointers: kcalloc(n, sizeof(void *), gfp)
|
||||
ALLOC_ARRAY(COUNT, (sizeof(void *)), GFP)
|
||||
|
|
||||
ALLOC_ARRAY((sizeof(void *)), COUNT, GFP)
|
||||
|
|
||||
// Convert any other expression: p = kcalloc(n, sizeof(*p), gfp)
|
||||
- ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
|
||||
+ ALLOC_OBJS(E, COUNT, GFP)
|
||||
|
|
||||
// Convert any other type: p = kcalloc(n, sizeof(struct item), gfp)
|
||||
- ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
|
||||
+ ALLOC_OBJS(TYPE, COUNT, GFP)
|
||||
|
|
||||
// The same, for arrays allocated as (size, count):
|
||||
// Exclude byte buffers and integral pointers: kcalloc(sizeof(u8), n, gfp),
|
||||
// kcalloc(sizeof(char *), n, gfp)
|
||||
ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(BYTE_TYPES)\|
|
||||
sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), COUNT, GFP)
|
||||
|
|
||||
// Exclude arrays assigned to a byte pointer:
|
||||
// u8 *buf = kcalloc(sizeof(*hdr), n, gfp)
|
||||
BYTES = ALLOC_ARRAY((\(sizeof(E)\|sizeof(TYPE)\)), COUNT, GFP)
|
||||
|
|
||||
// Convert a multi-byte array to a pointer to it:
|
||||
// u32 *p = kcalloc(sizeof(u32), n, gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC_ARRAY((sizeof(MULTIBYTE_TYPES)), COUNT, GFP)
|
||||
+ ALLOC_OBJS(MULTIBYTE_TYPES, COUNT, GFP)
|
||||
|
|
||||
// Same by expression: u32 *p = kcalloc(sizeof(*p), n, gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) =
|
||||
- ALLOC_ARRAY((sizeof(MULTIBYTE)), COUNT, GFP)
|
||||
+ ALLOC_OBJS(MULTIBYTE, COUNT, GFP)
|
||||
|
|
||||
// Exclude other multi-byte arrays:
|
||||
// s16 (*pairs)[2] = kcalloc(sizeof(s16), n, gfp)
|
||||
ALLOC_ARRAY((\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), COUNT, GFP)
|
||||
|
|
||||
// Exclude other arrays assigned to a multi-byte pointer:
|
||||
// u32 *p = kcalloc(sizeof(*hdr), n, gfp)
|
||||
\(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC_ARRAY((\(sizeof(E)\|sizeof(TYPE)\)), COUNT, GFP)
|
||||
|
|
||||
// Exclude arrays of void pointers: kcalloc(sizeof(void *), n, gfp)
|
||||
ALLOC_ARRAY((sizeof(void *)), COUNT, GFP)
|
||||
|
|
||||
// Convert any other expression: p = kcalloc(sizeof(*p), n, gfp)
|
||||
- ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
|
||||
+ ALLOC_OBJS(E, COUNT, GFP)
|
||||
|
|
||||
// Convert any other type: p = kcalloc(sizeof(struct item), n, gfp)
|
||||
- ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
|
||||
+ ALLOC_OBJS(TYPE, COUNT, GFP)
|
||||
|
|
||||
// Convert flexible array structures: p = kmalloc(struct_size(p, data, n), gfp)
|
||||
- ALLOC(struct_size(VAR, FLEX, COUNT), GFP)
|
||||
+ ALLOC_FLEX(*VAR, FLEX, COUNT, GFP)
|
||||
|
|
||||
// Same by type: kmalloc(struct_size_t(struct item, data, n), gfp)
|
||||
- ALLOC(struct_size_t(TYPE, FLEX, COUNT), GFP)
|
||||
+ ALLOC_FLEX(TYPE, FLEX, COUNT, GFP)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#define ORIG_TYPE_NAME(node) \
|
||||
(TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
|
||||
|
||||
#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS__)
|
||||
#define INFORM(loc, msg, ...) warning_at(loc, 0, "randstruct: " msg, ##__VA_ARGS__)
|
||||
#define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__)
|
||||
|
||||
__visible int plugin_is_GPL_compatible;
|
||||
|
|
@ -698,6 +698,63 @@ static void handle_local_var_initializers(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does @container reach a field of type @member_type by a chain of
|
||||
* by-value members? That is the relationship container_of() expresses --
|
||||
* its @member argument may be a dotted path, e.g.
|
||||
* container_of(inode, struct ceph_inode_info, netfs.inode) -- so a cast
|
||||
* from @member_type * to @container * is legitimate rather than a
|
||||
* layout-confusing one.
|
||||
*
|
||||
* container_of() used to leave a "void *__mptr" temporary behind, and this
|
||||
* pass recognised such casts by that name. Commit f9e7a7564834
|
||||
* ("container_of: remove local __mptr variable") removed it to stop nested
|
||||
* container_of() shadowing itself, and the cast now folds to a bare SSA
|
||||
* copy when the member sits at offset 0, leaving nothing syntactic to key
|
||||
* on. Match the type relationship instead.
|
||||
*
|
||||
* The depth bound keeps this cheap; container_of() paths are one or two
|
||||
* members deep in practice.
|
||||
*/
|
||||
#define CONTAINER_OF_MAX_DEPTH 4
|
||||
|
||||
static bool is_container_of_cast(const_tree container, const_tree member_type,
|
||||
int depth)
|
||||
{
|
||||
const_tree field;
|
||||
|
||||
if (container == NULL_TREE || depth > CONTAINER_OF_MAX_DEPTH)
|
||||
return false;
|
||||
|
||||
if (TREE_CODE(container) != RECORD_TYPE &&
|
||||
TREE_CODE(container) != UNION_TYPE)
|
||||
return false;
|
||||
|
||||
for (field = TYPE_FIELDS(container); field; field = DECL_CHAIN(field)) {
|
||||
const_tree field_type;
|
||||
|
||||
if (TREE_CODE(field) != FIELD_DECL)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Only a member at offset 0 can reach here: for any other
|
||||
* offset container_of()'s subtraction survives folding, the
|
||||
* cast's rhs stays void *, and the caller skipped it above.
|
||||
*/
|
||||
if (!integer_zerop(byte_position(field)))
|
||||
continue;
|
||||
|
||||
field_type = TYPE_MAIN_VARIANT(TREE_TYPE(field));
|
||||
if (field_type == member_type)
|
||||
return true;
|
||||
|
||||
if (is_container_of_cast(field_type, member_type, depth + 1))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* iterate over all statements to find "bad" casts:
|
||||
* those where the address of the start of a structure is cast
|
||||
|
|
@ -799,10 +856,8 @@ static unsigned int find_bad_casts_execute(void)
|
|||
#endif
|
||||
MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
|
||||
} else {
|
||||
const_tree ssa_name_var = SSA_NAME_VAR(rhs1);
|
||||
/* skip bogus type casts introduced by container_of */
|
||||
if (ssa_name_var != NULL_TREE && DECL_NAME(ssa_name_var) &&
|
||||
!strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr"))
|
||||
if (is_container_of_cast(ptr_lhs_type, ptr_rhs_type, 0))
|
||||
continue;
|
||||
#ifndef __DEBUG_PLUGIN
|
||||
if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type)))
|
||||
|
|
|
|||
|
|
@ -693,7 +693,7 @@ static ssize_t flags_show(struct device *dev,
|
|||
if (seq_buf_used(&s))
|
||||
seq_buf_printf(&s, "\n");
|
||||
|
||||
return seq_buf_used(&s);
|
||||
return seq_buf_strlen(&s);
|
||||
}
|
||||
static DEVICE_ATTR_RO(flags);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user