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:
Linus Torvalds 2026-10-22 15:01:57 -10:00
commit 864c07de0c
28 changed files with 1145 additions and 145 deletions

View File

@ -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>

View File

@ -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
-------

View File

@ -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

View File

@ -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);

View File

@ -93,6 +93,7 @@
.fini_array : {
__fini_array_start = .;
*(.fini_array)
*(.fini_array.*)
__fini_array_end = .;
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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
}

View File

@ -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;

View File

@ -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 "

View File

@ -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);

View File

@ -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)
{

View File

@ -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,

View File

@ -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);

View File

@ -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 */

View File

@ -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.

View File

@ -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--;

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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.
*/

View File

@ -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),
{}
};

View File

@ -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. */

View File

@ -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)
)

View File

@ -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)))

View File

@ -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);