diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:01:57 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:01:57 -1000 |
| commit | 864c07de0c7dcbca58b61625dfa2e461ae63b242 (patch) | |
| tree | 4d2c5f0962b95b99c8f5b04f62ff729086ac5f34 /include/linux | |
| parent | 5b193d6a8973655f19de87a7873ce31dee2d5fe6 (diff) | |
| parent | 2f70a9b7e9c74b92a85a2584090f0f5c4cbaa4be (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
...
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/device/devres.h | 102 | ||||
| -rw-r--r-- | include/linux/fortify-string.h | 2 | ||||
| -rw-r--r-- | include/linux/gfp.h | 4 | ||||
| -rw-r--r-- | include/linux/gfp_types.h | 4 | ||||
| -rw-r--r-- | include/linux/overflow.h | 2 | ||||
| -rw-r--r-- | include/linux/seq_buf.h | 141 | ||||
| -rw-r--r-- | include/linux/trace_seq.h | 5 |
7 files changed, 244 insertions, 16 deletions
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) { |
