summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:01:57 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:01:57 -1000
commit864c07de0c7dcbca58b61625dfa2e461ae63b242 (patch)
tree4d2c5f0962b95b99c8f5b04f62ff729086ac5f34
parent5b193d6a8973655f19de87a7873ce31dee2d5fe6 (diff)
parent2f70a9b7e9c74b92a85a2584090f0f5c4cbaa4be (diff)
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 ...
-rw-r--r--.get_maintainer.ignore1
-rw-r--r--Documentation/core-api/kernel-api.rst9
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/powerpc/platforms/pseries/papr_scm.c6
-rw-r--r--arch/um/include/asm/common.lds.S1
-rw-r--r--arch/x86/kernel/cpu/common.c3
-rw-r--r--drivers/extcon/extcon-rtk-type-c.c3
-rw-r--r--drivers/hwtracing/coresight/coresight-cti-sysfs.c2
-rw-r--r--drivers/misc/lkdtm/bugs.c4
-rw-r--r--drivers/misc/lkdtm/core.c16
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c45
-rw-r--r--fs/signalfd.c28
-rw-r--r--include/linux/device/devres.h102
-rw-r--r--include/linux/fortify-string.h2
-rw-r--r--include/linux/gfp.h4
-rw-r--r--include/linux/gfp_types.h4
-rw-r--r--include/linux/overflow.h2
-rw-r--r--include/linux/seq_buf.h141
-rw-r--r--include/linux/trace_seq.h5
-rw-r--r--kernel/bpf/diagnostics.c8
-rw-r--r--kernel/trace/trace_events.c4
-rw-r--r--kernel/trace/trace_events_hist.c6
-rw-r--r--lib/seq_buf.c37
-rw-r--r--lib/tests/seq_buf_kunit.c537
-rw-r--r--lib/tests/stackinit_kunit.c93
-rw-r--r--scripts/coccinelle/api/kmalloc_objs.cocci161
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c63
-rw-r--r--tools/testing/nvdimm/test/ndtest.c2
28 files changed, 1145 insertions, 145 deletions
diff --git a/.get_maintainer.ignore b/.get_maintainer.ignore
index d19b725fd803..a5946cb30cf9 100644
--- a/.get_maintainer.ignore
+++ b/.get_maintainer.ignore
@@ -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>
diff --git a/Documentation/core-api/kernel-api.rst b/Documentation/core-api/kernel-api.rst
index 4c4a57c1c094..f5a0aedbbb48 100644
--- a/Documentation/core-api/kernel-api.rst
+++ b/Documentation/core-api/kernel-api.rst
@@ -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
-------
diff --git a/MAINTAINERS b/MAINTAINERS
index 5b70859d117b..7ffd35e7faf0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index 75da96c08cdd..5c3bf06bb59b 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -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);
diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S
index 6585cb93e6eb..7b0c6f8a9d4f 100644
--- a/arch/um/include/asm/common.lds.S
+++ b/arch/um/include/asm/common.lds.S
@@ -93,6 +93,7 @@
.fini_array : {
__fini_array_start = .;
*(.fini_array)
+ *(.fini_array.*)
__fini_array_end = .;
}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 7d0b9bdc64cc..9bd50c68cd7e 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -490,9 +490,6 @@ set_register:
bits_changed);
}
}
-#if IS_MODULE(CONFIG_LKDTM)
-EXPORT_SYMBOL_GPL(native_write_cr4);
-#endif
void cr4_update_irqsoff(unsigned long set, unsigned long clear)
{
diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c
index 82b60b927e41..da0b3fd3676e 100644
--- a/drivers/extcon/extcon-rtk-type-c.c
+++ b/drivers/extcon/extcon-rtk-type-c.c
@@ -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;
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
index 3fe2c916d228..2ed5d8eb1dda 100644
--- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
@@ -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;
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index 3eca2ef64aff..d82c47fb9f03 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -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
}
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c
index ededa32d6744..01bebcb33bd4 100644
--- a/drivers/misc/lkdtm/core.c
+++ b/drivers/misc/lkdtm/core.c
@@ -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;
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index cfa4371169f1..a7b1ea67ed98 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -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 "
diff --git a/fs/signalfd.c b/fs/signalfd.c
index dff53745e352..22bc0870a824 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -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, &current->sighand->signalfd_wqh, wait);
spin_lock_irq(&current->sighand->siglock);
- if (next_signal(&current->pending, &ctx->sigmask) ||
- next_signal(&current->signal->shared_pending,
- &ctx->sigmask))
+ refine_sigmask(ctx, &sigmask);
+ if (next_signal(&current->pending, &sigmask) ||
+ next_signal(&current->signal->shared_pending, &sigmask))
events |= EPOLLIN;
spin_unlock_irq(&current->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(&current->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(&current->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(&current->sighand->siglock);
schedule();
spin_lock_irq(&current->sighand->siglock);
+ refine_sigmask(ctx, &sigmask);
}
spin_unlock_irq(&current->sighand->siglock);
diff --git a/include/linux/device/devres.h b/include/linux/device/devres.h
index 14ab9159bdda..7656c9ecd4da 100644
--- a/include/linux/device/devres.h
+++ b/include/linux/device/devres.h
@@ -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)
{
diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index cf841dc71fef..7e7c369e0a6c 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -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,
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 872bc53f32ec..b62e2fd53cb4 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -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);
diff --git a/include/linux/gfp_types.h b/include/linux/gfp_types.h
index 190191411009..b1cd143f3c42 100644
--- a/include/linux/gfp_types.h
+++ b/include/linux/gfp_types.h
@@ -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 */
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index a8cb6319b4fb..d0f04436343e 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.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.
diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 9f2839e73f8a..f006f2654e4c 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -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,18 +172,68 @@ 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
* @bufp: the beginning of the buffer is stored here
@@ -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--;
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
index 697d619aafdc..7174ebf3f015 100644
--- a/include/linux/trace_seq.h
+++ b/include/linux/trace_seq.h
@@ -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)
{
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index 0abbbe177e31..8f64bfd9afee 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -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;
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 30c0ddf90887..6428861102a1 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -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;
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index dcb5d7321095..5c07fee3a328 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -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);
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index a92093f346da..54b76044e4ba 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -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.
*/
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index eb466386bbef..fc53e9ef844c 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -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),
{}
};
diff --git a/lib/tests/stackinit_kunit.c b/lib/tests/stackinit_kunit.c
index ff2784769772..d589536afa20 100644
--- a/lib/tests/stackinit_kunit.c
+++ b/lib/tests/stackinit_kunit.c
@@ -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. */
diff --git a/scripts/coccinelle/api/kmalloc_objs.cocci b/scripts/coccinelle/api/kmalloc_objs.cocci
index e9a415b7b6f4..0a98ddf03eca 100644
--- a/scripts/coccinelle/api/kmalloc_objs.cocci
+++ b/scripts/coccinelle/api/kmalloc_objs.cocci
@@ -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)
-|
- BYTES = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
-|
- BYTES = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
-|
- BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(E)), 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_PTRS = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
+// Exclude arrays assigned to a byte pointer:
+// u8 *buf = kcalloc(n, sizeof(*hdr), gfp)
+ BYTES = ALLOC_ARRAY(COUNT, (\(sizeof(E)\|sizeof(TYPE)\)), GFP)
|
- ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), COUNT, 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 = ALLOC_ARRAY((sizeof(E)), COUNT, 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 = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
+// Exclude other multi-byte arrays:
+// s16 (*pairs)[2] = kcalloc(n, sizeof(s16), gfp)
+ ALLOC_ARRAY(COUNT, (\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), 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)
)
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
index ff65a4f87f24..1e66f45fe29b 100644
--- a/scripts/gcc-plugins/randomize_layout_plugin.c
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -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;
@@ -699,6 +699,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
* to a pointer of a structure of a different type, or a
@@ -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)))
diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c
index 2051ad5d4882..f097f2992966 100644
--- a/tools/testing/nvdimm/test/ndtest.c
+++ b/tools/testing/nvdimm/test/ndtest.c
@@ -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);