Merge tag 'overflow-v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Nov 2021 00:12:56 +0000 (17:12 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Nov 2021 00:12:56 +0000 (17:12 -0700)
Pull overflow updates from Kees Cook:
 "The end goal of the current buffer overflow detection work[0] is to
  gain full compile-time and run-time coverage of all detectable buffer
  overflows seen via array indexing or memcpy(), memmove(), and
  memset(). The str*() family of functions already have full coverage.

  While much of the work for these changes have been on-going for many
  releases (i.e. 0-element and 1-element array replacements, as well as
  avoiding false positives and fixing discovered overflows[1]), this
  series contains the foundational elements of several related buffer
  overflow detection improvements by providing new common helpers and
  FORTIFY_SOURCE changes needed to gain the introspection required for
  compiler visibility into array sizes. Also included are a handful of
  already Acked instances using the helpers (or related clean-ups), with
  many more waiting at the ready to be taken via subsystem-specific
  trees[2].

  The new helpers are:

   - struct_group() for gaining struct member range introspection

   - memset_after() and memset_startat() for clearing to the end of
     structures

   - DECLARE_FLEX_ARRAY() for using flex arrays in unions or alone in
     structs

  Also included is the beginning of the refactoring of FORTIFY_SOURCE to
  support memcpy() introspection, fix missing and regressed coverage
  under GCC, and to prepare to fix the currently broken Clang support.
  Finishing this work is part of the larger series[0], but depends on
  all the false positives and buffer overflow bug fixes to have landed
  already and those that depend on this series to land.

  As part of the FORTIFY_SOURCE refactoring, a set of both a
  compile-time and run-time tests are added for FORTIFY_SOURCE and the
  mem*()-family functions respectively. The compile time tests have
  found a legitimate (though corner-case) bug[6] already.

  Please note that the appearance of "panic" and "BUG" in the
  FORTIFY_SOURCE refactoring are the result of relocating existing code,
  and no new use of those code-paths are expected nor desired.

  Finally, there are two tree-wide conversions for 0-element arrays and
  flexible array unions to gain sane compiler introspection coverage
  that result in no known object code differences.

  After this series (and the changes that have now landed via netdev and
  usb), we are very close to finally being able to build with
  -Warray-bounds and -Wzero-length-bounds.

  However, due corner cases in GCC[3] and Clang[4], I have not included
  the last two patches that turn on these options, as I don't want to
  introduce any known warnings to the build. Hopefully these can be
  solved soon"

Link: https://lore.kernel.org/lkml/20210818060533.3569517-1-keescook@chromium.org/
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?qt=grep&q=FORTIFY_SOURCE
Link: https://lore.kernel.org/lkml/202108220107.3E26FE6C9C@keescook/
Link: https://lore.kernel.org/lkml/3ab153ec-2798-da4c-f7b1-81b0ac8b0c5b@roeck-us.net/
Link: https://bugs.llvm.org/show_bug.cgi?id=51682
Link: https://lore.kernel.org/lkml/202109051257.29B29745C0@keescook/
Link: https://lore.kernel.org/lkml/20211020200039.170424-1-keescook@chromium.org/
* tag 'overflow-v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (30 commits)
  fortify: strlen: Avoid shadowing previous locals
  compiler-gcc.h: Define __SANITIZE_ADDRESS__ under hwaddress sanitizer
  treewide: Replace 0-element memcpy() destinations with flexible arrays
  treewide: Replace open-coded flex arrays in unions
  stddef: Introduce DECLARE_FLEX_ARRAY() helper
  btrfs: Use memset_startat() to clear end of struct
  string.h: Introduce memset_startat() for wiping trailing members and padding
  xfrm: Use memset_after() to clear padding
  string.h: Introduce memset_after() for wiping trailing members/padding
  lib: Introduce CONFIG_MEMCPY_KUNIT_TEST
  fortify: Add compile-time FORTIFY_SOURCE tests
  fortify: Allow strlen() and strnlen() to pass compile-time known lengths
  fortify: Prepare to improve strnlen() and strlen() warnings
  fortify: Fix dropped strcpy() compile-time write overflow check
  fortify: Explicitly disable Clang support
  fortify: Move remaining fortify helpers into fortify-string.h
  lib/string: Move helper functions out of string.c
  compiler_types.h: Remove __compiletime_object_size()
  cm4000_cs: Use struct_group() to zero struct cm4000_dev region
  can: flexcan: Use struct_group() to zero struct flexcan_regs regions
  ...

76 files changed:
MAINTAINERS
arch/arm/boot/compressed/string.c
arch/s390/lib/string.c
arch/x86/boot/compressed/misc.h
arch/x86/boot/compressed/pgtable_64.c
arch/x86/lib/string_32.c
drivers/char/pcmcia/cm4000_cs.c
drivers/crypto/chelsio/chcr_crypto.h
drivers/cxl/cxl.h
drivers/gpu/drm/mga/mga_ioc32.c
drivers/hid/hid-cp2112.c
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-kone.h
drivers/iommu/amd/init.c
drivers/macintosh/smu.c
drivers/net/can/flexcan.c
drivers/net/can/usb/etas_es58x/es581_4.h
drivers/net/can/usb/etas_es58x/es58x_fd.h
drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c
drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h
drivers/net/wireless/ath/ath10k/bmi.h
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/intel/iwlegacy/commands.h
drivers/net/wireless/intel/iwlwifi/dvm/commands.h
drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
drivers/scsi/aic94xx/aic94xx_sds.c
drivers/scsi/ibmvscsi/ibmvscsi.c
drivers/scsi/qla4xxx/ql4_def.h
drivers/staging/r8188eu/include/ieee80211.h
drivers/staging/rtl8712/ieee80211.h
drivers/staging/rtl8723bs/include/ieee80211.h
fs/btrfs/root-tree.c
fs/hpfs/hpfs.h
include/linux/compiler-gcc.h
include/linux/compiler_types.h
include/linux/filter.h
include/linux/fortify-string.h
include/linux/ieee80211.h
include/linux/stddef.h
include/linux/string.h
include/linux/thread_info.h
include/scsi/sas.h
include/uapi/drm/mga_drm.h
include/uapi/linux/dlm_device.h
include/uapi/linux/stddef.h
include/uapi/rdma/rdma_user_rxe.h
include/uapi/sound/asoc.h
lib/.gitignore
lib/Kconfig.debug
lib/Makefile
lib/memcpy_kunit.c [new file with mode: 0644]
lib/string.c
lib/string_helpers.c
lib/test_fortify/read_overflow-memchr.c [new file with mode: 0644]
lib/test_fortify/read_overflow-memchr_inv.c [new file with mode: 0644]
lib/test_fortify/read_overflow-memcmp.c [new file with mode: 0644]
lib/test_fortify/read_overflow-memscan.c [new file with mode: 0644]
lib/test_fortify/read_overflow2-memcmp.c [new file with mode: 0644]
lib/test_fortify/read_overflow2-memcpy.c [new file with mode: 0644]
lib/test_fortify/read_overflow2-memmove.c [new file with mode: 0644]
lib/test_fortify/test_fortify.h [new file with mode: 0644]
lib/test_fortify/write_overflow-memcpy.c [new file with mode: 0644]
lib/test_fortify/write_overflow-memmove.c [new file with mode: 0644]
lib/test_fortify/write_overflow-memset.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strcpy-lit.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strcpy.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strlcpy-src.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strlcpy.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strncpy-src.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strncpy.c [new file with mode: 0644]
lib/test_fortify/write_overflow-strscpy.c [new file with mode: 0644]
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_user.c
scripts/kernel-doc
scripts/test_fortify.sh [new file with mode: 0644]
security/Kconfig

index 96a96b1..25978b6 100644 (file)
@@ -7341,6 +7341,15 @@ L:       netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/nvidia/*
 
+FORTIFY_SOURCE
+M:     Kees Cook <keescook@chromium.org>
+L:     linux-hardening@vger.kernel.org
+S:     Supported
+F:     include/linux/fortify-string.h
+F:     lib/test_fortify/*
+F:     scripts/test_fortify.sh
+K:     \b__NO_FORTIFY\b
+
 FPGA DFL DRIVERS
 M:     Wu Hao <hao.wu@intel.com>
 R:     Tom Rix <trix@redhat.com>
index 8c0fa27..fcc678f 100644 (file)
@@ -5,6 +5,7 @@
  * Small subset of simple string routines
  */
 
+#define __NO_FORTIFY
 #include <linux/string.h>
 
 /*
index a95ca6d..4708056 100644 (file)
@@ -8,6 +8,9 @@
  */
 
 #define IN_ARCH_STRING_C 1
+#ifndef __NO_FORTIFY
+# define __NO_FORTIFY
+#endif
 
 #include <linux/types.h>
 #include <linux/string.h>
index 3113925..49bde19 100644 (file)
@@ -14,6 +14,8 @@
 #undef CONFIG_KASAN
 #undef CONFIG_KASAN_GENERIC
 
+#define __NO_FORTIFY
+
 /* cpu_feature_enabled() cannot be used this early */
 #define USE_EARLY_PGTABLE_L5
 
index 2a78746..a173331 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "misc.h"
 #include <linux/efi.h>
 #include <asm/e820/types.h>
 #include <asm/processor.h>
index d15fdae..53b3f20 100644 (file)
@@ -11,6 +11,7 @@
  * strings.
  */
 
+#define __NO_FORTIFY
 #include <linux/string.h>
 #include <linux/export.h>
 
index 8f1bce0..adaec8f 100644 (file)
@@ -116,8 +116,9 @@ struct cm4000_dev {
        wait_queue_head_t atrq;         /* wait for ATR valid */
        wait_queue_head_t readq;        /* used by write to wake blk.read */
 
-       /* warning: do not move this fields.
+       /* warning: do not move this struct group.
         * initialising to zero depends on it - see ZERO_DEV below.  */
+       struct_group(init,
        unsigned char atr_csum;
        unsigned char atr_len_retry;
        unsigned short atr_len;
@@ -140,12 +141,10 @@ struct cm4000_dev {
 
        struct timer_list timer;        /* used to keep monitor running */
        int monitor_running;
+       );
 };
 
-#define        ZERO_DEV(dev)                                           \
-       memset(&dev->atr_csum,0,                                \
-               sizeof(struct cm4000_dev) -                     \
-               offsetof(struct cm4000_dev, atr_csum))
+#define        ZERO_DEV(dev)   memset(&((dev)->init), 0, sizeof((dev)->init))
 
 static struct pcmcia_device *dev_table[CM4000_MAX_DEV];
 static struct class *cmm_class;
index e89f9e0..c7816c8 100644 (file)
@@ -222,8 +222,10 @@ struct chcr_authenc_ctx {
 };
 
 struct __aead_ctx {
-       struct chcr_gcm_ctx gcm[0];
-       struct chcr_authenc_ctx authenc[];
+       union {
+               DECLARE_FLEX_ARRAY(struct chcr_gcm_ctx, gcm);
+               DECLARE_FLEX_ARRAY(struct chcr_authenc_ctx, authenc);
+       };
 };
 
 struct chcr_aead_ctx {
@@ -245,9 +247,11 @@ struct hmac_ctx {
 };
 
 struct __crypto_ctx {
-       struct hmac_ctx hmacctx[0];
-       struct ablk_ctx ablkctx[0];
-       struct chcr_aead_ctx aeadctx[];
+       union {
+               DECLARE_FLEX_ARRAY(struct hmac_ctx, hmacctx);
+               DECLARE_FLEX_ARRAY(struct ablk_ctx, ablkctx);
+               DECLARE_FLEX_ARRAY(struct chcr_aead_ctx, aeadctx);
+       };
 };
 
 struct chcr_context {
index 53927f9..9db0c40 100644 (file)
@@ -75,52 +75,27 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr)
 #define CXLDEV_MBOX_BG_CMD_STATUS_OFFSET 0x18
 #define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20
 
-#define CXL_COMPONENT_REGS() \
-       void __iomem *hdm_decoder
-
-#define CXL_DEVICE_REGS() \
-       void __iomem *status; \
-       void __iomem *mbox; \
-       void __iomem *memdev
-
-/* See note for 'struct cxl_regs' for the rationale of this organization */
-/*
- * CXL_COMPONENT_REGS - Common set of CXL Component register block base pointers
- * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure
- */
-struct cxl_component_regs {
-       CXL_COMPONENT_REGS();
-};
-
-/* See note for 'struct cxl_regs' for the rationale of this organization */
-/*
- * CXL_DEVICE_REGS - Common set of CXL Device register block base pointers
- * @status: CXL 2.0 8.2.8.3 Device Status Registers
- * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers
- * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers
- */
-struct cxl_device_regs {
-       CXL_DEVICE_REGS();
-};
-
 /*
- * Note, the anonymous union organization allows for per
- * register-block-type helper routines, without requiring block-type
- * agnostic code to include the prefix.
+ * Using struct_group() allows for per register-block-type helper routines,
+ * without requiring block-type agnostic code to include the prefix.
  */
 struct cxl_regs {
-       union {
-               struct {
-                       CXL_COMPONENT_REGS();
-               };
-               struct cxl_component_regs component;
-       };
-       union {
-               struct {
-                       CXL_DEVICE_REGS();
-               };
-               struct cxl_device_regs device_regs;
-       };
+       /*
+        * Common set of CXL Component register block base pointers
+        * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure
+        */
+       struct_group_tagged(cxl_component_regs, component,
+               void __iomem *hdm_decoder;
+       );
+       /*
+        * Common set of CXL Device register block base pointers
+        * @status: CXL 2.0 8.2.8.3 Device Status Registers
+        * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers
+        * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers
+        */
+       struct_group_tagged(cxl_device_regs, device_regs,
+               void __iomem *status, *mbox, *memdev;
+       );
 };
 
 struct cxl_reg_map {
index 4fd4de1..8944729 100644 (file)
 typedef struct drm32_mga_init {
        int func;
        u32 sarea_priv_offset;
-       int chipset;
-       int sgram;
-       unsigned int maccess;
-       unsigned int fb_cpp;
-       unsigned int front_offset, front_pitch;
-       unsigned int back_offset, back_pitch;
-       unsigned int depth_cpp;
-       unsigned int depth_offset, depth_pitch;
-       unsigned int texture_offset[MGA_NR_TEX_HEAPS];
-       unsigned int texture_size[MGA_NR_TEX_HEAPS];
+       struct_group(always32bit,
+               int chipset;
+               int sgram;
+               unsigned int maccess;
+               unsigned int fb_cpp;
+               unsigned int front_offset, front_pitch;
+               unsigned int back_offset, back_pitch;
+               unsigned int depth_cpp;
+               unsigned int depth_offset, depth_pitch;
+               unsigned int texture_offset[MGA_NR_TEX_HEAPS];
+               unsigned int texture_size[MGA_NR_TEX_HEAPS];
+       );
        u32 fb_offset;
        u32 mmio_offset;
        u32 status_offset;
@@ -67,9 +69,8 @@ static int compat_mga_init(struct file *file, unsigned int cmd,
 
        init.func = init32.func;
        init.sarea_priv_offset = init32.sarea_priv_offset;
-       memcpy(&init.chipset, &init32.chipset,
-               offsetof(drm_mga_init_t, fb_offset) -
-               offsetof(drm_mga_init_t, chipset));
+       memcpy(&init.always32bit, &init32.always32bit,
+              sizeof(init32.always32bit));
        init.fb_offset = init32.fb_offset;
        init.mmio_offset = init32.mmio_offset;
        init.status_offset = init32.status_offset;
index 477baa3..ece147d 100644 (file)
@@ -129,10 +129,12 @@ struct cp2112_xfer_status_report {
 
 struct cp2112_string_report {
        u8 dummy;               /* force .string to be aligned */
-       u8 report;              /* CP2112_*_STRING */
-       u8 length;              /* length in bytes of everyting after .report */
-       u8 type;                /* USB_DT_STRING */
-       wchar_t string[30];     /* UTF16_LITTLE_ENDIAN string */
+       struct_group_attr(contents, __packed,
+               u8 report;              /* CP2112_*_STRING */
+               u8 length;              /* length in bytes of everything after .report */
+               u8 type;                /* USB_DT_STRING */
+               wchar_t string[30];     /* UTF16_LITTLE_ENDIAN string */
+       );
 } __packed;
 
 /* Number of times to request transfer status before giving up waiting for a
@@ -986,8 +988,8 @@ static ssize_t pstr_show(struct device *kdev,
        u8 length;
        int ret;
 
-       ret = cp2112_hid_get(hdev, attr->report, &report.report,
-                            sizeof(report) - 1, HID_FEATURE_REPORT);
+       ret = cp2112_hid_get(hdev, attr->report, (u8 *)&report.contents,
+                            sizeof(report.contents), HID_FEATURE_REPORT);
        if (ret < 3) {
                hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
                        ret);
index 1ca6448..ea17abc 100644 (file)
@@ -857,7 +857,7 @@ static int kone_raw_event(struct hid_device *hdev, struct hid_report *report,
                memcpy(&kone->last_mouse_event, event,
                                sizeof(struct kone_mouse_event));
        else
-               memset(&event->tilt, 0, 5);
+               memset(&event->wipe, 0, sizeof(event->wipe));
 
        kone_keep_values_up_to_date(kone, event);
 
index 4a1a9cb..65c800e 100644 (file)
@@ -152,11 +152,13 @@ struct kone_mouse_event {
        uint16_t x;
        uint16_t y;
        uint8_t wheel; /* up = 1, down = -1 */
-       uint8_t tilt; /* right = 1, left = -1 */
-       uint8_t unknown;
-       uint8_t event;
-       uint8_t value; /* press = 0, release = 1 */
-       uint8_t macro_key; /* 0 to 8 */
+       struct_group(wipe,
+               uint8_t tilt; /* right = 1, left = -1 */
+               uint8_t unknown;
+               uint8_t event;
+               uint8_t value; /* press = 0, release = 1 */
+               uint8_t macro_key; /* 0 to 8 */
+       );
 } __attribute__ ((__packed__));
 
 enum kone_mouse_events {
index c6c53e1..1eacd43 100644 (file)
@@ -121,8 +121,10 @@ struct ivhd_entry {
        u8 type;
        u16 devid;
        u8 flags;
-       u32 ext;
-       u32 hidh;
+       struct_group(ext_hid,
+               u32 ext;
+               u32 hidh;
+       );
        u64 cid;
        u8 uidf;
        u8 uidl;
@@ -1377,7 +1379,8 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
                                break;
                        }
 
-                       memcpy(hid, (u8 *)(&e->ext), ACPIHID_HID_LEN - 1);
+                       BUILD_BUG_ON(sizeof(e->ext_hid) != ACPIHID_HID_LEN - 1);
+                       memcpy(hid, &e->ext_hid, ACPIHID_HID_LEN - 1);
                        hid[ACPIHID_HID_LEN - 1] = '\0';
 
                        if (!(*hid)) {
index fe63d5e..d33913d 100644 (file)
@@ -848,7 +848,8 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
        cmd->read = cmd->info.devaddr & 0x01;
        switch(cmd->info.type) {
        case SMU_I2C_TRANSFER_SIMPLE:
-               memset(&cmd->info.sublen, 0, 4);
+               cmd->info.sublen = 0;
+               memset(cmd->info.subaddr, 0, sizeof(cmd->info.subaddr));
                break;
        case SMU_I2C_TRANSFER_COMBINED:
                cmd->info.devaddr &= 0xfe;
index 7734229..12b60ad 100644 (file)
@@ -290,31 +290,33 @@ struct flexcan_regs {
        u32 dbg1;               /* 0x58 */
        u32 dbg2;               /* 0x5c */
        u32 _reserved3[8];      /* 0x60 */
-       u8 mb[2][512];          /* 0x80 - Not affected by Soft Reset */
-       /* FIFO-mode:
-        *                      MB
-        * 0x080...0x08f        0       RX message buffer
-        * 0x090...0x0df        1-5     reserved
-        * 0x0e0...0x0ff        6-7     8 entry ID table
-        *                              (mx25, mx28, mx35, mx53)
-        * 0x0e0...0x2df        6-7..37 8..128 entry ID table
-        *                              size conf'ed via ctrl2::RFFN
-        *                              (mx6, vf610)
-        */
-       u32 _reserved4[256];    /* 0x480 */
-       u32 rximr[64];          /* 0x880 - Not affected by Soft Reset */
-       u32 _reserved5[24];     /* 0x980 */
-       u32 gfwr_mx6;           /* 0x9e0 - MX6 */
-       u32 _reserved6[39];     /* 0x9e4 */
-       u32 _rxfir[6];          /* 0xa80 */
-       u32 _reserved8[2];      /* 0xa98 */
-       u32 _rxmgmask;          /* 0xaa0 */
-       u32 _rxfgmask;          /* 0xaa4 */
-       u32 _rx14mask;          /* 0xaa8 */
-       u32 _rx15mask;          /* 0xaac */
-       u32 tx_smb[4];          /* 0xab0 */
-       u32 rx_smb0[4];         /* 0xac0 */
-       u32 rx_smb1[4];         /* 0xad0 */
+       struct_group(init,
+               u8 mb[2][512];          /* 0x80 - Not affected by Soft Reset */
+               /* FIFO-mode:
+                *                      MB
+                * 0x080...0x08f        0       RX message buffer
+                * 0x090...0x0df        1-5     reserved
+                * 0x0e0...0x0ff        6-7     8 entry ID table
+                *                              (mx25, mx28, mx35, mx53)
+                * 0x0e0...0x2df        6-7..37 8..128 entry ID table
+                *                              size conf'ed via ctrl2::RFFN
+                *                              (mx6, vf610)
+                */
+               u32 _reserved4[256];    /* 0x480 */
+               u32 rximr[64];          /* 0x880 - Not affected by Soft Reset */
+               u32 _reserved5[24];     /* 0x980 */
+               u32 gfwr_mx6;           /* 0x9e0 - MX6 */
+               u32 _reserved6[39];     /* 0x9e4 */
+               u32 _rxfir[6];          /* 0xa80 */
+               u32 _reserved8[2];      /* 0xa98 */
+               u32 _rxmgmask;          /* 0xaa0 */
+               u32 _rxfgmask;          /* 0xaa4 */
+               u32 _rx14mask;          /* 0xaa8 */
+               u32 _rx15mask;          /* 0xaac */
+               u32 tx_smb[4];          /* 0xab0 */
+               u32 rx_smb0[4];         /* 0xac0 */
+               u32 rx_smb1[4];         /* 0xad0 */
+       );
        u32 mecr;               /* 0xae0 */
        u32 erriar;             /* 0xae4 */
        u32 erridpr;            /* 0xae8 */
@@ -328,9 +330,11 @@ struct flexcan_regs {
        u32 fdcbt;              /* 0xc04 - Not affected by Soft Reset */
        u32 fdcrc;              /* 0xc08 */
        u32 _reserved9[199];    /* 0xc0c */
-       u32 tx_smb_fd[18];      /* 0xf28 */
-       u32 rx_smb0_fd[18];     /* 0xf70 */
-       u32 rx_smb1_fd[18];     /* 0xfb8 */
+       struct_group(init_fd,
+               u32 tx_smb_fd[18];      /* 0xf28 */
+               u32 rx_smb0_fd[18];     /* 0xf70 */
+               u32 rx_smb1_fd[18];     /* 0xfb8 */
+       );
 };
 
 static_assert(sizeof(struct flexcan_regs) ==  0x4 * 18 + 0xfb8);
@@ -1400,14 +1404,10 @@ static void flexcan_ram_init(struct net_device *dev)
        reg_ctrl2 |= FLEXCAN_CTRL2_WRMFRZ;
        priv->write(reg_ctrl2, &regs->ctrl2);
 
-       memset_io(&regs->mb[0][0], 0,
-                 offsetof(struct flexcan_regs, rx_smb1[3]) -
-                 offsetof(struct flexcan_regs, mb[0][0]) + 0x4);
+       memset_io(&regs->init, 0, sizeof(regs->init));
 
        if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
-               memset_io(&regs->tx_smb_fd[0], 0,
-                         offsetof(struct flexcan_regs, rx_smb1_fd[17]) -
-                         offsetof(struct flexcan_regs, tx_smb_fd[0]) + 0x4);
+               memset_io(&regs->init_fd, 0, sizeof(regs->init_fd));
 
        reg_ctrl2 &= ~FLEXCAN_CTRL2_WRMFRZ;
        priv->write(reg_ctrl2, &regs->ctrl2);
index 4bc60a6..667ecb7 100644 (file)
@@ -192,7 +192,7 @@ struct es581_4_urb_cmd {
                struct es581_4_rx_cmd_ret rx_cmd_ret;
                __le64 timestamp;
                u8 rx_cmd_ret_u8;
-               u8 raw_msg[0];
+               DECLARE_FLEX_ARRAY(u8, raw_msg);
        } __packed;
 
        __le16 reserved_for_crc16_do_not_use;
index a191891..c4b19a6 100644 (file)
@@ -219,7 +219,7 @@ struct es58x_fd_urb_cmd {
                struct es58x_fd_tx_ack_msg tx_ack_msg;
                __le64 timestamp;
                __le32 rx_cmd_ret_le32;
-               u8 raw_msg[0];
+               DECLARE_FLEX_ARRAY(u8, raw_msg);
        } __packed;
 
        __le16 reserved_for_crc16_do_not_use;
index 228a5db..217ff59 100644 (file)
@@ -159,10 +159,10 @@ static int bnxt_hwrm_queue_cos2bw_qcfg(struct bnxt *bp, struct ieee_ets *ets)
        }
 
        data = &resp->queue_id0 + offsetof(struct bnxt_cos2bw_cfg, queue_id);
-       for (i = 0; i < bp->max_tc; i++, data += sizeof(cos2bw) - 4) {
+       for (i = 0; i < bp->max_tc; i++, data += sizeof(cos2bw.cfg)) {
                int tc;
 
-               memcpy(&cos2bw.queue_id, data, sizeof(cos2bw) - 4);
+               memcpy(&cos2bw.cfg, data, sizeof(cos2bw.cfg));
                if (i == 0)
                        cos2bw.queue_id = resp->queue_id0;
 
index 6eed231..7167425 100644 (file)
@@ -23,13 +23,15 @@ struct bnxt_dcb {
 
 struct bnxt_cos2bw_cfg {
        u8                      pad[3];
-       u8                      queue_id;
-       __le32                  min_bw;
-       __le32                  max_bw;
+       struct_group_attr(cfg, __packed,
+               u8              queue_id;
+               __le32          min_bw;
+               __le32          max_bw;
 #define BW_VALUE_UNIT_PERCENT1_100             (0x1UL << 29)
-       u8                      tsa;
-       u8                      pri_lvl;
-       u8                      bw_weight;
+               u8              tsa;
+               u8              pri_lvl;
+               u8              bw_weight;
+       );
        u8                      unused;
 };
 
index f6fadcb..0685c0d 100644 (file)
@@ -109,7 +109,7 @@ struct bmi_cmd {
                struct {
                        __le32 addr;
                        __le32 len;
-                       u8 payload[0];
+                       u8 payload[];
                } write_mem;
                struct {
                        __le32 addr;
@@ -138,18 +138,18 @@ struct bmi_cmd {
                } rompatch_uninstall;
                struct {
                        __le32 count;
-                       __le32 patch_ids[0]; /* length of @count */
+                       __le32 patch_ids[]; /* length of @count */
                } rompatch_activate;
                struct {
                        __le32 count;
-                       __le32 patch_ids[0]; /* length of @count */
+                       __le32 patch_ids[]; /* length of @count */
                } rompatch_deactivate;
                struct {
                        __le32 addr;
                } lz_start;
                struct {
                        __le32 len; /* max BMI_MAX_DATA_SIZE */
-                       u8 payload[0]; /* length of @len */
+                       u8 payload[]; /* length of @len */
                } lz_data;
                struct {
                        u8 name[BMI_NVRAM_SEG_NAME_SZ];
@@ -160,7 +160,7 @@ struct bmi_cmd {
 
 union bmi_resp {
        struct {
-               u8 payload[0];
+               DECLARE_FLEX_ARRAY(u8, payload);
        } read_mem;
        struct {
                __le32 result;
index ec689e3..a6de08d 100644 (file)
@@ -1674,8 +1674,11 @@ struct htt_tx_fetch_ind {
        __le32 token;
        __le16 num_resp_ids;
        __le16 num_records;
-       __le32 resp_ids[0]; /* ath10k_htt_get_tx_fetch_ind_resp_ids() */
-       struct htt_tx_fetch_record records[];
+       union {
+               /* ath10k_htt_get_tx_fetch_ind_resp_ids() */
+               DECLARE_FLEX_ARRAY(__le32, resp_ids);
+               DECLARE_FLEX_ARRAY(struct htt_tx_fetch_record, records);
+       };
 } __packed;
 
 static inline void *
index 89c6671..4a97310 100644 (file)
@@ -1408,8 +1408,10 @@ struct il3945_tx_cmd {
         * MAC header goes here, followed by 2 bytes padding if MAC header
         * length is 26 or 30 bytes, followed by payload data
         */
-       u8 payload[0];
-       struct ieee80211_hdr hdr[];
+       union {
+               DECLARE_FLEX_ARRAY(u8, payload);
+               DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr);
+       };
 } __packed;
 
 /*
index 235c7a2..75a4b8e 100644 (file)
@@ -1251,8 +1251,10 @@ struct iwl_tx_cmd {
         * MAC header goes here, followed by 2 bytes padding if MAC header
         * length is 26 or 30 bytes, followed by payload data
         */
-       u8 payload[0];
-       struct ieee80211_hdr hdr[];
+       union {
+               DECLARE_FLEX_ARRAY(u8, payload);
+               DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr);
+       };
 } __packed;
 
 /*
index 24e4a82..5fddfd3 100644 (file)
@@ -239,8 +239,10 @@ struct iwl_tx_cmd {
        u8 tid_tspec;
        __le16 pm_frame_timeout;
        __le16 reserved4;
-       u8 payload[0];
-       struct ieee80211_hdr hdr[0];
+       union {
+               DECLARE_FLEX_ARRAY(u8, payload);
+               DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr);
+       };
 } __packed; /* TX_CMD_API_S_VER_6 */
 
 struct iwl_dram_sec_info {
@@ -713,8 +715,10 @@ struct iwl_mvm_compressed_ba_notif {
        __le32 tx_rate;
        __le16 tfd_cnt;
        __le16 ra_tid_cnt;
-       struct iwl_mvm_compressed_ba_ratid ra_tid[0];
-       struct iwl_mvm_compressed_ba_tfd tfd[];
+       union {
+               DECLARE_FLEX_ARRAY(struct iwl_mvm_compressed_ba_ratid, ra_tid);
+               DECLARE_FLEX_ARRAY(struct iwl_mvm_compressed_ba_tfd, tfd);
+       };
 } __packed; /* COMPRESSED_BA_RES_API_S_VER_4 */
 
 /**
index 46815e6..5def83c 100644 (file)
@@ -517,8 +517,10 @@ struct asd_ms_conn_map {
        u8    num_nodes;
        u8    usage_model_id;
        u32   _resvd;
-       struct asd_ms_conn_desc conn_desc[0];
-       struct asd_ms_node_desc node_desc[];
+       union {
+               DECLARE_FLEX_ARRAY(struct asd_ms_conn_desc, conn_desc);
+               DECLARE_FLEX_ARRAY(struct asd_ms_node_desc, node_desc);
+       };
 } __attribute__ ((packed));
 
 struct asd_ctrla_phy_entry {
index 50df7dd..ea8e01f 100644 (file)
@@ -1055,8 +1055,9 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
                return SCSI_MLQUEUE_HOST_BUSY;
 
        /* Set up the actual SRP IU */
+       BUILD_BUG_ON(sizeof(evt_struct->iu.srp) != SRP_MAX_IU_LEN);
+       memset(&evt_struct->iu.srp, 0x00, sizeof(evt_struct->iu.srp));
        srp_cmd = &evt_struct->iu.srp.cmd;
-       memset(srp_cmd, 0x00, SRP_MAX_IU_LEN);
        srp_cmd->opcode = SRP_CMD;
        memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb));
        int_to_scsilun(lun, &srp_cmd->lun);
index 031569c..69a5905 100644 (file)
@@ -366,13 +366,13 @@ struct qla4_work_evt {
                struct {
                        enum iscsi_host_event_code code;
                        uint32_t data_size;
-                       uint8_t data[0];
+                       uint8_t data[];
                } aen;
                struct {
                        uint32_t status;
                        uint32_t pid;
                        uint32_t data_size;
-                       uint8_t data[0];
+                       uint8_t data[];
                } ping;
        } u;
 };
index bc5b030..9204dd4 100644 (file)
@@ -185,7 +185,7 @@ struct ieee_param {
                struct {
                        u32 len;
                        u8 reserved[32];
-                       u8 data[0];
+                       u8 data[];
                } wpa_ie;
                struct {
                        int command;
@@ -198,7 +198,7 @@ struct ieee_param {
                        u8 idx;
                        u8 seq[8]; /* sequence counter (set: RX, get: TX) */
                        u16 key_len;
-                       u8 key[0];
+                       u8 key[];
                } crypt;
 #ifdef CONFIG_88EU_AP_MODE
                struct {
@@ -210,7 +210,7 @@ struct ieee_param {
                } add_sta;
                struct {
                        u8      reserved[2];/* for set max_num_sta */
-                       u8      buf[0];
+                       u8      buf[];
                } bcn_ie;
 #endif
 
index 61eff7c..65ceaca 100644 (file)
@@ -78,7 +78,7 @@ struct ieee_param {
                struct {
                        u32 len;
                        u8 reserved[32];
-                       u8 data[0];
+                       u8 data[];
                } wpa_ie;
                struct {
                        int command;
@@ -91,7 +91,7 @@ struct ieee_param {
                        u8 idx;
                        u8 seq[8]; /* sequence counter (set: RX, get: TX) */
                        u16 key_len;
-                       u8 key[0];
+                       u8 key[];
                } crypt;
        } u;
 };
index d6236f5..c11d7e2 100644 (file)
@@ -172,7 +172,7 @@ struct ieee_param {
                struct {
                        u32 len;
                        u8 reserved[32];
-                       u8 data[0];
+                       u8 data[];
                } wpa_ie;
                struct{
                        int command;
@@ -185,7 +185,7 @@ struct ieee_param {
                        u8 idx;
                        u8 seq[8]; /* sequence counter (set: RX, get: TX) */
                        u16 key_len;
-                       u8 key[0];
+                       u8 key[];
                } crypt;
                struct {
                        u16 aid;
@@ -196,7 +196,7 @@ struct ieee_param {
                } add_sta;
                struct {
                        u8 reserved[2];/* for set max_num_sta */
-                       u8 buf[0];
+                       u8 buf[];
                } bcn_ie;
        } u;
 };
index 702dc54..12ceb14 100644 (file)
@@ -39,10 +39,8 @@ static void btrfs_read_root_item(struct extent_buffer *eb, int slot,
                need_reset = 1;
        }
        if (need_reset) {
-               memset(&item->generation_v2, 0,
-                       sizeof(*item) - offsetof(struct btrfs_root_item,
-                                       generation_v2));
-
+               /* Clear all members from generation_v2 onwards. */
+               memset_startat(item, 0, generation_v2);
                generate_random_guid(item->uuid);
        }
 }
index d92c4af..281dec8 100644 (file)
@@ -409,10 +409,10 @@ struct bplus_header
   __le16 first_free;                   /* offset from start of header to
                                           first free node in array */
   union {
-    struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
-                                              subtree pointers */
-    struct bplus_leaf_node external[0];            /* (external) 3-word entries giving
-                                              sector runs */
+       /* (internal) 2-word entries giving subtree pointers */
+       DECLARE_FLEX_ARRAY(struct bplus_internal_node, internal);
+       /* (external) 3-word entries giving sector runs */
+       DECLARE_FLEX_ARRAY(struct bplus_leaf_node, external);
   } u;
 };
 
index bd2b881..7bbd8df 100644 (file)
@@ -41,8 +41,6 @@
 
 #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
 
-#define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
-
 #if defined(LATENT_ENTROPY_PLUGIN) && !defined(__CHECKER__)
 #define __latent_entropy __attribute__((latent_entropy))
 #endif
 #define __no_sanitize_coverage
 #endif
 
+/*
+ * Treat __SANITIZE_HWADDRESS__ the same as __SANITIZE_ADDRESS__ in the kernel,
+ * matching the defines used by Clang.
+ */
+#ifdef __SANITIZE_HWADDRESS__
+#define __SANITIZE_ADDRESS__
+#endif
+
 /*
  * Turn individual warnings and errors on and off locally, depending
  * on version.
index b6ff83a..05ceb2e 100644 (file)
@@ -290,11 +290,6 @@ struct ftrace_likely_data {
        (sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
         sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
 
-/* Compile time object size, -1 for unknown */
-#ifndef __compiletime_object_size
-# define __compiletime_object_size(obj) -1
-#endif
-
 #ifdef __OPTIMIZE__
 # define __compiletime_assert(condition, msg, prefix, suffix)          \
        do {                                                            \
index ef03ff3..b956eeb 100644 (file)
@@ -586,8 +586,10 @@ struct bpf_prog {
        struct bpf_prog_aux     *aux;           /* Auxiliary fields */
        struct sock_fprog_kern  *orig_prog;     /* Original BPF program */
        /* Instructions for interpreter */
-       struct sock_filter      insns[0];
-       struct bpf_insn         insnsi[];
+       union {
+               DECLARE_FLEX_ARRAY(struct sock_filter, insns);
+               DECLARE_FLEX_ARRAY(struct bpf_insn, insnsi);
+       };
 };
 
 struct sk_filter {
index c1be374..a6cd681 100644 (file)
@@ -2,6 +2,27 @@
 #ifndef _LINUX_FORTIFY_STRING_H_
 #define _LINUX_FORTIFY_STRING_H_
 
+#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline))
+#define __RENAME(x) __asm__(#x)
+
+void fortify_panic(const char *name) __noreturn __cold;
+void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)");
+void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)");
+void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
+
+#define __compiletime_strlen(p)                                        \
+({                                                             \
+       unsigned char *__p = (unsigned char *)(p);              \
+       size_t __ret = (size_t)-1;                              \
+       size_t __p_size = __builtin_object_size(p, 1);          \
+       if (__p_size != (size_t)-1) {                           \
+               size_t __p_len = __p_size - 1;                  \
+               if (__builtin_constant_p(__p[__p_len]) &&       \
+                   __p[__p_len] == '\0')                       \
+                       __ret = __builtin_strlen(__p);          \
+       }                                                       \
+       __ret;                                                  \
+})
 
 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 extern void *__underlying_memchr(const void *p, int c, __kernel_size_t size) __RENAME(memchr);
@@ -49,28 +70,38 @@ __FORTIFY_INLINE char *strcat(char *p, const char *q)
        return p;
 }
 
-__FORTIFY_INLINE __kernel_size_t strlen(const char *p)
+extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
+__FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
 {
-       __kernel_size_t ret;
        size_t p_size = __builtin_object_size(p, 1);
+       size_t p_len = __compiletime_strlen(p);
+       size_t ret;
 
-       /* Work around gcc excess stack consumption issue */
-       if (p_size == (size_t)-1 ||
-               (__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0'))
-               return __underlying_strlen(p);
-       ret = strnlen(p, p_size);
-       if (p_size <= ret)
+       /* We can take compile-time actions when maxlen is const. */
+       if (__builtin_constant_p(maxlen) && p_len != (size_t)-1) {
+               /* If p is const, we can use its compile-time-known len. */
+               if (maxlen >= p_size)
+                       return p_len;
+       }
+
+       /* Do not check characters beyond the end of p. */
+       ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
+       if (p_size <= ret && maxlen != ret)
                fortify_panic(__func__);
        return ret;
 }
 
-extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
-__FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
+/* defined after fortified strnlen to reuse it. */
+__FORTIFY_INLINE __kernel_size_t strlen(const char *p)
 {
+       __kernel_size_t ret;
        size_t p_size = __builtin_object_size(p, 1);
-       __kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
 
-       if (p_size <= ret && maxlen != ret)
+       /* Give up if we don't know how large p is. */
+       if (p_size == (size_t)-1)
+               return __underlying_strlen(p);
+       ret = strnlen(p, p_size);
+       if (p_size <= ret)
                fortify_panic(__func__);
        return ret;
 }
@@ -79,24 +110,27 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen)
 extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy);
 __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
 {
-       size_t ret;
        size_t p_size = __builtin_object_size(p, 1);
        size_t q_size = __builtin_object_size(q, 1);
+       size_t q_len;   /* Full count of source string length. */
+       size_t len;     /* Count of characters going into destination. */
 
        if (p_size == (size_t)-1 && q_size == (size_t)-1)
                return __real_strlcpy(p, q, size);
-       ret = strlen(q);
-       if (size) {
-               size_t len = (ret >= size) ? size - 1 : ret;
-
-               if (__builtin_constant_p(len) && len >= p_size)
+       q_len = strlen(q);
+       len = (q_len >= size) ? size - 1 : q_len;
+       if (__builtin_constant_p(size) && __builtin_constant_p(q_len) && size) {
+               /* Write size is always larger than destination. */
+               if (len >= p_size)
                        __write_overflow();
+       }
+       if (size) {
                if (len >= p_size)
                        fortify_panic(__func__);
                __underlying_memcpy(p, q, len);
                p[len] = '\0';
        }
-       return ret;
+       return q_len;
 }
 
 /* defined after fortified strnlen to reuse it */
@@ -280,7 +314,10 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
        if (p_size == (size_t)-1 && q_size == (size_t)-1)
                return __underlying_strcpy(p, q);
        size = strlen(q) + 1;
-       /* test here to use the more stringent object size */
+       /* Compile-time check for const size overflow. */
+       if (__builtin_constant_p(size) && p_size < size)
+               __write_overflow();
+       /* Run-time check for dynamic size overflow. */
        if (p_size < size)
                fortify_panic(__func__);
        memcpy(p, q, size);
index 6942645..ada3dd7 100644 (file)
@@ -1143,7 +1143,7 @@ struct ieee80211_mgmt {
                        __le16 auth_transaction;
                        __le16 status_code;
                        /* possibly followed by Challenge text */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed auth;
                struct {
                        __le16 reason_code;
@@ -1152,26 +1152,26 @@ struct ieee80211_mgmt {
                        __le16 capab_info;
                        __le16 listen_interval;
                        /* followed by SSID and Supported rates */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed assoc_req;
                struct {
                        __le16 capab_info;
                        __le16 status_code;
                        __le16 aid;
                        /* followed by Supported rates */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed assoc_resp, reassoc_resp;
                struct {
                        __le16 capab_info;
                        __le16 status_code;
-                       u8 variable[0];
+                       u8 variable[];
                } __packed s1g_assoc_resp, s1g_reassoc_resp;
                struct {
                        __le16 capab_info;
                        __le16 listen_interval;
                        u8 current_ap[ETH_ALEN];
                        /* followed by SSID and Supported rates */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed reassoc_req;
                struct {
                        __le16 reason_code;
@@ -1182,11 +1182,11 @@ struct ieee80211_mgmt {
                        __le16 capab_info;
                        /* followed by some of SSID, Supported rates,
                         * FH Params, DS Params, CF Params, IBSS Params, TIM */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed beacon;
                struct {
                        /* only variable items: SSID, Supported rates */
-                       u8 variable[0];
+                       DECLARE_FLEX_ARRAY(u8, variable);
                } __packed probe_req;
                struct {
                        __le64 timestamp;
@@ -1194,7 +1194,7 @@ struct ieee80211_mgmt {
                        __le16 capab_info;
                        /* followed by some of SSID, Supported rates,
                         * FH Params, DS Params, CF Params, IBSS Params */
-                       u8 variable[0];
+                       u8 variable[];
                } __packed probe_resp;
                struct {
                        u8 category;
@@ -1203,16 +1203,16 @@ struct ieee80211_mgmt {
                                        u8 action_code;
                                        u8 dialog_token;
                                        u8 status_code;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed wme_action;
                                struct{
                                        u8 action_code;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed chan_switch;
                                struct{
                                        u8 action_code;
                                        struct ieee80211_ext_chansw_ie data;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed ext_chan_switch;
                                struct{
                                        u8 action_code;
@@ -1228,7 +1228,7 @@ struct ieee80211_mgmt {
                                        __le16 timeout;
                                        __le16 start_seq_num;
                                        /* followed by BA Extension */
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed addba_req;
                                struct{
                                        u8 action_code;
@@ -1244,11 +1244,11 @@ struct ieee80211_mgmt {
                                } __packed delba;
                                struct {
                                        u8 action_code;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed self_prot;
                                struct{
                                        u8 action_code;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed mesh_action;
                                struct {
                                        u8 action;
@@ -1292,7 +1292,7 @@ struct ieee80211_mgmt {
                                        u8 toa[6];
                                        __le16 tod_error;
                                        __le16 toa_error;
-                                       u8 variable[0];
+                                       u8 variable[];
                                } __packed ftm;
                                struct {
                                        u8 action_code;
index 998a4ba..ca507bd 100644 (file)
@@ -20,7 +20,7 @@ enum {
 #endif
 
 /**
- * sizeof_field(TYPE, MEMBER)
+ * sizeof_field() - Report the size of a struct field in bytes
  *
  * @TYPE: The structure containing the field of interest
  * @MEMBER: The field to return the size of
@@ -28,7 +28,7 @@ enum {
 #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
 
 /**
- * offsetofend(TYPE, MEMBER)
+ * offsetofend() - Report the offset of a struct field within the struct
  *
  * @TYPE: The type of the structure
  * @MEMBER: The member within the structure to get the end offset of
@@ -36,4 +36,65 @@ enum {
 #define offsetofend(TYPE, MEMBER) \
        (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
 
+/**
+ * struct_group() - Wrap a set of declarations in a mirrored struct
+ *
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical
+ * layout and size: one anonymous and one named. The former can be
+ * used normally without sub-struct naming, and the latter can be
+ * used to reason about the start, end, and size of the group of
+ * struct members.
+ */
+#define struct_group(NAME, MEMBERS...) \
+       __struct_group(/* no tag */, NAME, /* no attrs */, MEMBERS)
+
+/**
+ * struct_group_attr() - Create a struct_group() with trailing attributes
+ *
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @ATTRS: Any struct attributes to apply
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical
+ * layout and size: one anonymous and one named. The former can be
+ * used normally without sub-struct naming, and the latter can be
+ * used to reason about the start, end, and size of the group of
+ * struct members. Includes structure attributes argument.
+ */
+#define struct_group_attr(NAME, ATTRS, MEMBERS...) \
+       __struct_group(/* no tag */, NAME, ATTRS, MEMBERS)
+
+/**
+ * struct_group_tagged() - Create a struct_group with a reusable tag
+ *
+ * @TAG: The tag name for the named sub-struct
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical
+ * layout and size: one anonymous and one named. The former can be
+ * used normally without sub-struct naming, and the latter can be
+ * used to reason about the start, end, and size of the group of
+ * struct members. Includes struct tag argument for the named copy,
+ * so the specified layout can be reused later.
+ */
+#define struct_group_tagged(TAG, NAME, MEMBERS...) \
+       __struct_group(TAG, NAME, /* no attrs */, MEMBERS)
+
+/**
+ * DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define DECLARE_FLEX_ARRAY(TYPE, NAME) \
+       __DECLARE_FLEX_ARRAY(TYPE, NAME)
+
 #endif
index 5e96d65..5a36608 100644 (file)
@@ -249,15 +249,6 @@ static inline const char *kbasename(const char *path)
        return tail ? tail + 1 : path;
 }
 
-#define __FORTIFY_INLINE extern __always_inline __attribute__((gnu_inline))
-#define __RENAME(x) __asm__(#x)
-
-void fortify_panic(const char *name) __noreturn __cold;
-void __read_overflow(void) __compiletime_error("detected read beyond size of object passed as 1st parameter");
-void __read_overflow2(void) __compiletime_error("detected read beyond size of object passed as 2nd parameter");
-void __read_overflow3(void) __compiletime_error("detected read beyond size of object passed as 3rd parameter");
-void __write_overflow(void) __compiletime_error("detected write beyond size of object passed as 1st parameter");
-
 #if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
 #include <linux/fortify-string.h>
 #endif
@@ -280,6 +271,41 @@ static inline void memcpy_and_pad(void *dest, size_t dest_len,
                memcpy(dest, src, dest_len);
 }
 
+/**
+ * memset_after - Set a value after a struct member to the end of a struct
+ *
+ * @obj: Address of target struct instance
+ * @v: Byte value to repeatedly write
+ * @member: after which struct member to start writing bytes
+ *
+ * This is good for clearing padding following the given member.
+ */
+#define memset_after(obj, v, member)                                   \
+({                                                                     \
+       u8 *__ptr = (u8 *)(obj);                                        \
+       typeof(v) __val = (v);                                          \
+       memset(__ptr + offsetofend(typeof(*(obj)), member), __val,      \
+              sizeof(*(obj)) - offsetofend(typeof(*(obj)), member));   \
+})
+
+/**
+ * memset_startat - Set a value starting at a member to the end of a struct
+ *
+ * @obj: Address of target struct instance
+ * @v: Byte value to repeatedly write
+ * @member: struct member to start writing at
+ *
+ * Note that if there is padding between the prior member and the target
+ * member, memset_after() should be used to clear the prior padding.
+ */
+#define memset_startat(obj, v, member)                                 \
+({                                                                     \
+       u8 *__ptr = (u8 *)(obj);                                        \
+       typeof(v) __val = (v);                                          \
+       memset(__ptr + offsetof(typeof(*(obj)), member), __val,         \
+              sizeof(*(obj)) - offsetof(typeof(*(obj)), member));      \
+})
+
 /**
  * str_has_prefix - Test if a string has a given prefix
  * @str: The string to test
index 0999f63..ad0c4e0 100644 (file)
@@ -203,7 +203,7 @@ static inline void copy_overflow(int size, unsigned long count)
 static __always_inline __must_check bool
 check_copy_size(const void *addr, size_t bytes, bool is_source)
 {
-       int sz = __compiletime_object_size(addr);
+       int sz = __builtin_object_size(addr, 0);
        if (unlikely(sz >= 0 && sz < bytes)) {
                if (!__builtin_constant_p(bytes))
                        copy_overflow(sz, bytes);
index 4726c1b..64154c1 100644 (file)
@@ -323,8 +323,10 @@ struct ssp_response_iu {
        __be32 sense_data_len;
        __be32 response_data_len;
 
-       u8     resp_data[0];
-       u8     sense_data[];
+       union {
+               DECLARE_FLEX_ARRAY(u8, resp_data);
+               DECLARE_FLEX_ARRAY(u8, sense_data);
+       };
 } __attribute__ ((packed));
 
 struct ssp_command_iu {
@@ -554,8 +556,10 @@ struct ssp_response_iu {
        __be32 sense_data_len;
        __be32 response_data_len;
 
-       u8     resp_data[0];
-       u8     sense_data[];
+       union {
+               DECLARE_FLEX_ARRAY(u8, resp_data);
+               DECLARE_FLEX_ARRAY(u8, sense_data);
+       };
 } __attribute__ ((packed));
 
 struct ssp_command_iu {
index 8c43375..bb31567 100644 (file)
@@ -279,20 +279,22 @@ typedef struct drm_mga_init {
 
        unsigned long sarea_priv_offset;
 
-       int chipset;
-       int sgram;
+       __struct_group(/* no tag */, always32bit, /* no attrs */,
+               int chipset;
+               int sgram;
 
-       unsigned int maccess;
+               unsigned int maccess;
 
-       unsigned int fb_cpp;
-       unsigned int front_offset, front_pitch;
-       unsigned int back_offset, back_pitch;
+               unsigned int fb_cpp;
+               unsigned int front_offset, front_pitch;
+               unsigned int back_offset, back_pitch;
 
-       unsigned int depth_cpp;
-       unsigned int depth_offset, depth_pitch;
+               unsigned int depth_cpp;
+               unsigned int depth_offset, depth_pitch;
 
-       unsigned int texture_offset[MGA_NR_TEX_HEAPS];
-       unsigned int texture_size[MGA_NR_TEX_HEAPS];
+               unsigned int texture_offset[MGA_NR_TEX_HEAPS];
+               unsigned int texture_size[MGA_NR_TEX_HEAPS];
+       );
 
        unsigned long fb_offset;
        unsigned long mmio_offset;
index f880d28..e83954c 100644 (file)
@@ -45,13 +45,13 @@ struct dlm_lock_params {
        void __user *bastaddr;
        struct dlm_lksb __user *lksb;
        char lvb[DLM_USER_LVB_LEN];
-       char name[0];
+       char name[];
 };
 
 struct dlm_lspace_params {
        __u32 flags;
        __u32 minor;
-       char name[0];
+       char name[];
 };
 
 struct dlm_purge_params {
index ee8220f..3021ea2 100644 (file)
@@ -4,3 +4,40 @@
 #ifndef __always_inline
 #define __always_inline inline
 #endif
+
+/**
+ * __struct_group() - Create a mirrored named and anonyomous struct
+ *
+ * @TAG: The tag name for the named sub-struct (usually empty)
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @ATTRS: Any struct attributes (usually empty)
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical layout
+ * and size: one anonymous and one named. The former's members can be used
+ * normally without sub-struct naming, and the latter can be used to
+ * reason about the start, end, and size of the group of struct members.
+ * The named struct can also be explicitly tagged for layer reuse, as well
+ * as both having struct attributes appended.
+ */
+#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
+       union { \
+               struct { MEMBERS } ATTRS; \
+               struct TAG { MEMBERS } ATTRS NAME; \
+       }
+
+/**
+ * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define __DECLARE_FLEX_ARRAY(TYPE, NAME)       \
+       struct { \
+               struct { } __empty_ ## NAME; \
+               TYPE NAME[]; \
+       }
index e283c22..7f44d54 100644 (file)
@@ -141,8 +141,8 @@ struct rxe_dma_info {
        __u32                   sge_offset;
        __u32                   reserved;
        union {
-               __u8            inline_data[0];
-               struct rxe_sge  sge[0];
+               __DECLARE_FLEX_ARRAY(__u8, inline_data);
+               __DECLARE_FLEX_ARRAY(struct rxe_sge, sge);
        };
 };
 
index da61398..0539492 100644 (file)
@@ -240,8 +240,8 @@ struct snd_soc_tplg_vendor_array {
 struct snd_soc_tplg_private {
        __le32 size;    /* in bytes of private data */
        union {
-               char data[0];
-               struct snd_soc_tplg_vendor_array array[0];
+               __DECLARE_FLEX_ARRAY(char, data);
+               __DECLARE_FLEX_ARRAY(struct snd_soc_tplg_vendor_array, array);
        };
 } __attribute__((packed));
 
index 5e7fa54..e5e217b 100644 (file)
@@ -4,3 +4,5 @@
 /gen_crc32table
 /gen_crc64table
 /oid_registry_data.c
+/test_fortify.log
+/test_fortify/*.log
index 40e4766..1b99aea 100644 (file)
@@ -2452,6 +2452,17 @@ config RATIONAL_KUNIT_TEST
 
          If unsure, say N.
 
+config MEMCPY_KUNIT_TEST
+       tristate "Test memcpy(), memmove(), and memset() functions at runtime" if !KUNIT_ALL_TESTS
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         Builds unit tests for memcpy(), memmove(), and memset() functions.
+         For more information on KUnit and unit tests in general please refer
+         to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+         If unsure, say N.
+
 config TEST_UDELAY
        tristate "udelay test driver"
        help
index a841be5..b0cb894 100644 (file)
@@ -358,5 +358,39 @@ obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
 obj-$(CONFIG_BITS_TEST) += test_bits.o
 obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o
 obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
+obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
 
 obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
+
+# FORTIFY_SOURCE compile-time behavior tests
+TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
+TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
+TEST_FORTIFY_LOG = test_fortify.log
+
+quiet_cmd_test_fortify = TEST    $@
+      cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \
+                       $< $@ "$(NM)" $(CC) $(c_flags) \
+                       $(call cc-disable-warning,fortify-source)
+
+targets += $(TEST_FORTIFY_LOGS)
+clean-files += $(TEST_FORTIFY_LOGS)
+clean-files += $(addsuffix .o, $(TEST_FORTIFY_LOGS))
+$(obj)/test_fortify/%.log: $(src)/test_fortify/%.c \
+                          $(src)/test_fortify/test_fortify.h \
+                          $(srctree)/include/linux/fortify-string.h \
+                          $(srctree)/scripts/test_fortify.sh \
+                          FORCE
+       $(call if_changed,test_fortify)
+
+quiet_cmd_gen_fortify_log = GEN     $@
+      cmd_gen_fortify_log = cat </dev/null $(filter-out FORCE,$^) 2>/dev/null > $@ || true
+
+targets += $(TEST_FORTIFY_LOG)
+clean-files += $(TEST_FORTIFY_LOG)
+$(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
+       $(call if_changed,gen_fortify_log)
+
+# Fake dependency to trigger the fortify tests.
+ifeq ($(CONFIG_FORTIFY_SOURCE),y)
+$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
+endif
diff --git a/lib/memcpy_kunit.c b/lib/memcpy_kunit.c
new file mode 100644 (file)
index 0000000..62f8ffc
--- /dev/null
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test cases for memcpy(), memmove(), and memset().
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <kunit/test.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+struct some_bytes {
+       union {
+               u8 data[32];
+               struct {
+                       u32 one;
+                       u16 two;
+                       u8  three;
+                       /* 1 byte hole */
+                       u32 four[4];
+               };
+       };
+};
+
+#define check(instance, v) do {        \
+       int i;  \
+       BUILD_BUG_ON(sizeof(instance.data) != 32);      \
+       for (i = 0; i < sizeof(instance.data); i++) {   \
+               KUNIT_ASSERT_EQ_MSG(test, instance.data[i], v, \
+                       "line %d: '%s' not initialized to 0x%02x @ %d (saw 0x%02x)\n", \
+                       __LINE__, #instance, v, i, instance.data[i]);   \
+       }       \
+} while (0)
+
+#define compare(name, one, two) do { \
+       int i; \
+       BUILD_BUG_ON(sizeof(one) != sizeof(two)); \
+       for (i = 0; i < sizeof(one); i++) {     \
+               KUNIT_EXPECT_EQ_MSG(test, one.data[i], two.data[i], \
+                       "line %d: %s.data[%d] (0x%02x) != %s.data[%d] (0x%02x)\n", \
+                       __LINE__, #one, i, one.data[i], #two, i, two.data[i]); \
+       }       \
+       kunit_info(test, "ok: " TEST_OP "() " name "\n");       \
+} while (0)
+
+static void memcpy_test(struct kunit *test)
+{
+#define TEST_OP "memcpy"
+       struct some_bytes control = {
+               .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       },
+       };
+       struct some_bytes zero = { };
+       struct some_bytes middle = {
+               .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       },
+       };
+       struct some_bytes three = {
+               .data = { 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       },
+       };
+       struct some_bytes dest = { };
+       int count;
+       u8 *ptr;
+
+       /* Verify static initializers. */
+       check(control, 0x20);
+       check(zero, 0);
+       compare("static initializers", dest, zero);
+
+       /* Verify assignment. */
+       dest = control;
+       compare("direct assignment", dest, control);
+
+       /* Verify complete overwrite. */
+       memcpy(dest.data, zero.data, sizeof(dest.data));
+       compare("complete overwrite", dest, zero);
+
+       /* Verify middle overwrite. */
+       dest = control;
+       memcpy(dest.data + 12, zero.data, 7);
+       compare("middle overwrite", dest, middle);
+
+       /* Verify argument side-effects aren't repeated. */
+       dest = control;
+       ptr = dest.data;
+       count = 1;
+       memcpy(ptr++, zero.data, count++);
+       ptr += 8;
+       memcpy(ptr++, zero.data, count++);
+       compare("argument side-effects", dest, three);
+#undef TEST_OP
+}
+
+static void memmove_test(struct kunit *test)
+{
+#define TEST_OP "memmove"
+       struct some_bytes control = {
+               .data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                       },
+       };
+       struct some_bytes zero = { };
+       struct some_bytes middle = {
+               .data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                       },
+       };
+       struct some_bytes five = {
+               .data = { 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                       },
+       };
+       struct some_bytes overlap = {
+               .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                       },
+       };
+       struct some_bytes overlap_expected = {
+               .data = { 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x07,
+                         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+                       },
+       };
+       struct some_bytes dest = { };
+       int count;
+       u8 *ptr;
+
+       /* Verify static initializers. */
+       check(control, 0x99);
+       check(zero, 0);
+       compare("static initializers", zero, dest);
+
+       /* Verify assignment. */
+       dest = control;
+       compare("direct assignment", dest, control);
+
+       /* Verify complete overwrite. */
+       memmove(dest.data, zero.data, sizeof(dest.data));
+       compare("complete overwrite", dest, zero);
+
+       /* Verify middle overwrite. */
+       dest = control;
+       memmove(dest.data + 12, zero.data, 7);
+       compare("middle overwrite", dest, middle);
+
+       /* Verify argument side-effects aren't repeated. */
+       dest = control;
+       ptr = dest.data;
+       count = 2;
+       memmove(ptr++, zero.data, count++);
+       ptr += 9;
+       memmove(ptr++, zero.data, count++);
+       compare("argument side-effects", dest, five);
+
+       /* Verify overlapping overwrite is correct. */
+       ptr = &overlap.data[2];
+       memmove(ptr, overlap.data, 5);
+       compare("overlapping write", overlap, overlap_expected);
+#undef TEST_OP
+}
+
+static void memset_test(struct kunit *test)
+{
+#define TEST_OP "memset"
+       struct some_bytes control = {
+               .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                       },
+       };
+       struct some_bytes complete = {
+               .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       },
+       };
+       struct some_bytes middle = {
+               .data = { 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
+                         0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+                         0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                       },
+       };
+       struct some_bytes three = {
+               .data = { 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x61, 0x61, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                       },
+       };
+       struct some_bytes after = {
+               .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x72,
+                         0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+                         0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+                         0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+                       },
+       };
+       struct some_bytes startat = {
+               .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+                         0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+                         0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+                         0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+                       },
+       };
+       struct some_bytes dest = { };
+       int count, value;
+       u8 *ptr;
+
+       /* Verify static initializers. */
+       check(control, 0x30);
+       check(dest, 0);
+
+       /* Verify assignment. */
+       dest = control;
+       compare("direct assignment", dest, control);
+
+       /* Verify complete overwrite. */
+       memset(dest.data, 0xff, sizeof(dest.data));
+       compare("complete overwrite", dest, complete);
+
+       /* Verify middle overwrite. */
+       dest = control;
+       memset(dest.data + 4, 0x31, 16);
+       compare("middle overwrite", dest, middle);
+
+       /* Verify argument side-effects aren't repeated. */
+       dest = control;
+       ptr = dest.data;
+       value = 0x60;
+       count = 1;
+       memset(ptr++, value++, count++);
+       ptr += 8;
+       memset(ptr++, value++, count++);
+       compare("argument side-effects", dest, three);
+
+       /* Verify memset_after() */
+       dest = control;
+       memset_after(&dest, 0x72, three);
+       compare("memset_after()", dest, after);
+
+       /* Verify memset_startat() */
+       dest = control;
+       memset_startat(&dest, 0x79, four);
+       compare("memset_startat()", dest, startat);
+#undef TEST_OP
+}
+
+static struct kunit_case memcpy_test_cases[] = {
+       KUNIT_CASE(memset_test),
+       KUNIT_CASE(memcpy_test),
+       KUNIT_CASE(memmove_test),
+       {}
+};
+
+static struct kunit_suite memcpy_test_suite = {
+       .name = "memcpy",
+       .test_cases = memcpy_test_cases,
+};
+
+kunit_test_suite(memcpy_test_suite);
+
+MODULE_LICENSE("GPL");
index b2de45a..485777c 100644 (file)
@@ -6,20 +6,15 @@
  */
 
 /*
- * stupid library routines.. The optimized versions should generally be found
- * as inline code in <asm-xx/string.h>
+ * This file should be used only for "library" routines that may have
+ * alternative implementations on specific architectures (generally
+ * found in <asm-xx/string.h>), or get overloaded by FORTIFY_SOURCE.
+ * (Specifically, this file is built with __NO_FORTIFY.)
  *
- * These are buggy as well..
- *
- * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
- * -  Added strsep() which will replace strtok() soon (because strsep() is
- *    reentrant and should be faster). Use only strsep() in new code, please.
- *
- * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
- *                    Matthew Hawkins <matt@mh.dropbear.id.au>
- * -  Kissed strtok() goodbye
+ * Other helper functions should live in string_helpers.c.
  */
 
+#define __NO_FORTIFY
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
@@ -238,40 +233,6 @@ ssize_t strscpy(char *dest, const char *src, size_t count)
 EXPORT_SYMBOL(strscpy);
 #endif
 
-/**
- * strscpy_pad() - Copy a C-string into a sized buffer
- * @dest: Where to copy the string to
- * @src: Where to copy the string from
- * @count: Size of destination buffer
- *
- * Copy the string, or as much of it as fits, into the dest buffer.  The
- * behavior is undefined if the string buffers overlap.  The destination
- * buffer is always %NUL terminated, unless it's zero-sized.
- *
- * If the source string is shorter than the destination buffer, zeros
- * the tail of the destination buffer.
- *
- * For full explanation of why you may want to consider using the
- * 'strscpy' functions please see the function docstring for strscpy().
- *
- * Returns:
- * * The number of characters copied (not including the trailing %NUL)
- * * -E2BIG if count is 0 or @src was truncated.
- */
-ssize_t strscpy_pad(char *dest, const char *src, size_t count)
-{
-       ssize_t written;
-
-       written = strscpy(dest, src, count);
-       if (written < 0 || written == count - 1)
-               return written;
-
-       memset(dest + written + 1, 0, count - written - 1);
-
-       return written;
-}
-EXPORT_SYMBOL(strscpy_pad);
-
 /**
  * stpcpy - copy a string from src to dest returning a pointer to the new end
  *          of dest, including src's %NUL-terminator. May overrun dest.
@@ -514,46 +475,6 @@ char *strnchr(const char *s, size_t count, int c)
 EXPORT_SYMBOL(strnchr);
 #endif
 
-/**
- * skip_spaces - Removes leading whitespace from @str.
- * @str: The string to be stripped.
- *
- * Returns a pointer to the first non-whitespace character in @str.
- */
-char *skip_spaces(const char *str)
-{
-       while (isspace(*str))
-               ++str;
-       return (char *)str;
-}
-EXPORT_SYMBOL(skip_spaces);
-
-/**
- * strim - Removes leading and trailing whitespace from @s.
- * @s: The string to be stripped.
- *
- * Note that the first trailing whitespace is replaced with a %NUL-terminator
- * in the given string @s. Returns a pointer to the first non-whitespace
- * character in @s.
- */
-char *strim(char *s)
-{
-       size_t size;
-       char *end;
-
-       size = strlen(s);
-       if (!size)
-               return s;
-
-       end = s + size - 1;
-       while (end >= s && isspace(*end))
-               end--;
-       *(end + 1) = '\0';
-
-       return skip_spaces(s);
-}
-EXPORT_SYMBOL(strim);
-
 #ifndef __HAVE_ARCH_STRLEN
 /**
  * strlen - Find the length of a string
@@ -688,101 +609,6 @@ char *strsep(char **s, const char *ct)
 EXPORT_SYMBOL(strsep);
 #endif
 
-/**
- * sysfs_streq - return true if strings are equal, modulo trailing newline
- * @s1: one string
- * @s2: another string
- *
- * This routine returns true iff two strings are equal, treating both
- * NUL and newline-then-NUL as equivalent string terminations.  It's
- * geared for use with sysfs input strings, which generally terminate
- * with newlines but are compared against values without newlines.
- */
-bool sysfs_streq(const char *s1, const char *s2)
-{
-       while (*s1 && *s1 == *s2) {
-               s1++;
-               s2++;
-       }
-
-       if (*s1 == *s2)
-               return true;
-       if (!*s1 && *s2 == '\n' && !s2[1])
-               return true;
-       if (*s1 == '\n' && !s1[1] && !*s2)
-               return true;
-       return false;
-}
-EXPORT_SYMBOL(sysfs_streq);
-
-/**
- * match_string - matches given string in an array
- * @array:     array of strings
- * @n:         number of strings in the array or -1 for NULL terminated arrays
- * @string:    string to match with
- *
- * This routine will look for a string in an array of strings up to the
- * n-th element in the array or until the first NULL element.
- *
- * Historically the value of -1 for @n, was used to search in arrays that
- * are NULL terminated. However, the function does not make a distinction
- * when finishing the search: either @n elements have been compared OR
- * the first NULL element was found.
- *
- * Return:
- * index of a @string in the @array if matches, or %-EINVAL otherwise.
- */
-int match_string(const char * const *array, size_t n, const char *string)
-{
-       int index;
-       const char *item;
-
-       for (index = 0; index < n; index++) {
-               item = array[index];
-               if (!item)
-                       break;
-               if (!strcmp(item, string))
-                       return index;
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL(match_string);
-
-/**
- * __sysfs_match_string - matches given string in an array
- * @array: array of strings
- * @n: number of strings in the array or -1 for NULL terminated arrays
- * @str: string to match with
- *
- * Returns index of @str in the @array or -EINVAL, just like match_string().
- * Uses sysfs_streq instead of strcmp for matching.
- *
- * This routine will look for a string in an array of strings up to the
- * n-th element in the array or until the first NULL element.
- *
- * Historically the value of -1 for @n, was used to search in arrays that
- * are NULL terminated. However, the function does not make a distinction
- * when finishing the search: either @n elements have been compared OR
- * the first NULL element was found.
- */
-int __sysfs_match_string(const char * const *array, size_t n, const char *str)
-{
-       const char *item;
-       int index;
-
-       for (index = 0; index < n; index++) {
-               item = array[index];
-               if (!item)
-                       break;
-               if (sysfs_streq(item, str))
-                       return index;
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL(__sysfs_match_string);
-
 #ifndef __HAVE_ARCH_MEMSET
 /**
  * memset - Fill a region of memory with the given value
@@ -1141,27 +967,3 @@ void *memchr_inv(const void *start, int c, size_t bytes)
        return check_bytes8(start, value, bytes % 8);
 }
 EXPORT_SYMBOL(memchr_inv);
-
-/**
- * strreplace - Replace all occurrences of character in string.
- * @s: The string to operate on.
- * @old: The character being replaced.
- * @new: The character @old is replaced with.
- *
- * Returns pointer to the nul byte at the end of @s.
- */
-char *strreplace(char *s, char old, char new)
-{
-       for (; *s; ++s)
-               if (*s == old)
-                       *s = new;
-       return s;
-}
-EXPORT_SYMBOL(strreplace);
-
-void fortify_panic(const char *name)
-{
-       pr_emerg("detected buffer overflow in %s\n", name);
-       BUG();
-}
-EXPORT_SYMBOL(fortify_panic);
index 3806a52..faa9d8e 100644 (file)
@@ -696,3 +696,198 @@ void kfree_strarray(char **array, size_t n)
        kfree(array);
 }
 EXPORT_SYMBOL_GPL(kfree_strarray);
+
+/**
+ * strscpy_pad() - Copy a C-string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: Size of destination buffer
+ *
+ * Copy the string, or as much of it as fits, into the dest buffer.  The
+ * behavior is undefined if the string buffers overlap.  The destination
+ * buffer is always %NUL terminated, unless it's zero-sized.
+ *
+ * If the source string is shorter than the destination buffer, zeros
+ * the tail of the destination buffer.
+ *
+ * For full explanation of why you may want to consider using the
+ * 'strscpy' functions please see the function docstring for strscpy().
+ *
+ * Returns:
+ * * The number of characters copied (not including the trailing %NUL)
+ * * -E2BIG if count is 0 or @src was truncated.
+ */
+ssize_t strscpy_pad(char *dest, const char *src, size_t count)
+{
+       ssize_t written;
+
+       written = strscpy(dest, src, count);
+       if (written < 0 || written == count - 1)
+               return written;
+
+       memset(dest + written + 1, 0, count - written - 1);
+
+       return written;
+}
+EXPORT_SYMBOL(strscpy_pad);
+
+/**
+ * skip_spaces - Removes leading whitespace from @str.
+ * @str: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @str.
+ */
+char *skip_spaces(const char *str)
+{
+       while (isspace(*str))
+               ++str;
+       return (char *)str;
+}
+EXPORT_SYMBOL(skip_spaces);
+
+/**
+ * strim - Removes leading and trailing whitespace from @s.
+ * @s: The string to be stripped.
+ *
+ * Note that the first trailing whitespace is replaced with a %NUL-terminator
+ * in the given string @s. Returns a pointer to the first non-whitespace
+ * character in @s.
+ */
+char *strim(char *s)
+{
+       size_t size;
+       char *end;
+
+       size = strlen(s);
+       if (!size)
+               return s;
+
+       end = s + size - 1;
+       while (end >= s && isspace(*end))
+               end--;
+       *(end + 1) = '\0';
+
+       return skip_spaces(s);
+}
+EXPORT_SYMBOL(strim);
+
+/**
+ * sysfs_streq - return true if strings are equal, modulo trailing newline
+ * @s1: one string
+ * @s2: another string
+ *
+ * This routine returns true iff two strings are equal, treating both
+ * NUL and newline-then-NUL as equivalent string terminations.  It's
+ * geared for use with sysfs input strings, which generally terminate
+ * with newlines but are compared against values without newlines.
+ */
+bool sysfs_streq(const char *s1, const char *s2)
+{
+       while (*s1 && *s1 == *s2) {
+               s1++;
+               s2++;
+       }
+
+       if (*s1 == *s2)
+               return true;
+       if (!*s1 && *s2 == '\n' && !s2[1])
+               return true;
+       if (*s1 == '\n' && !s1[1] && !*s2)
+               return true;
+       return false;
+}
+EXPORT_SYMBOL(sysfs_streq);
+
+/**
+ * match_string - matches given string in an array
+ * @array:     array of strings
+ * @n:         number of strings in the array or -1 for NULL terminated arrays
+ * @string:    string to match with
+ *
+ * This routine will look for a string in an array of strings up to the
+ * n-th element in the array or until the first NULL element.
+ *
+ * Historically the value of -1 for @n, was used to search in arrays that
+ * are NULL terminated. However, the function does not make a distinction
+ * when finishing the search: either @n elements have been compared OR
+ * the first NULL element was found.
+ *
+ * Return:
+ * index of a @string in the @array if matches, or %-EINVAL otherwise.
+ */
+int match_string(const char * const *array, size_t n, const char *string)
+{
+       int index;
+       const char *item;
+
+       for (index = 0; index < n; index++) {
+               item = array[index];
+               if (!item)
+                       break;
+               if (!strcmp(item, string))
+                       return index;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(match_string);
+
+/**
+ * __sysfs_match_string - matches given string in an array
+ * @array: array of strings
+ * @n: number of strings in the array or -1 for NULL terminated arrays
+ * @str: string to match with
+ *
+ * Returns index of @str in the @array or -EINVAL, just like match_string().
+ * Uses sysfs_streq instead of strcmp for matching.
+ *
+ * This routine will look for a string in an array of strings up to the
+ * n-th element in the array or until the first NULL element.
+ *
+ * Historically the value of -1 for @n, was used to search in arrays that
+ * are NULL terminated. However, the function does not make a distinction
+ * when finishing the search: either @n elements have been compared OR
+ * the first NULL element was found.
+ */
+int __sysfs_match_string(const char * const *array, size_t n, const char *str)
+{
+       const char *item;
+       int index;
+
+       for (index = 0; index < n; index++) {
+               item = array[index];
+               if (!item)
+                       break;
+               if (sysfs_streq(item, str))
+                       return index;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(__sysfs_match_string);
+
+/**
+ * strreplace - Replace all occurrences of character in string.
+ * @s: The string to operate on.
+ * @old: The character being replaced.
+ * @new: The character @old is replaced with.
+ *
+ * Returns pointer to the nul byte at the end of @s.
+ */
+char *strreplace(char *s, char old, char new)
+{
+       for (; *s; ++s)
+               if (*s == old)
+                       *s = new;
+       return s;
+}
+EXPORT_SYMBOL(strreplace);
+
+#ifdef CONFIG_FORTIFY_SOURCE
+void fortify_panic(const char *name)
+{
+       pr_emerg("detected buffer overflow in %s\n", name);
+       BUG();
+}
+EXPORT_SYMBOL(fortify_panic);
+#endif /* CONFIG_FORTIFY_SOURCE */
diff --git a/lib/test_fortify/read_overflow-memchr.c b/lib/test_fortify/read_overflow-memchr.c
new file mode 100644 (file)
index 0000000..2743084
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memchr(small, 0x7A, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow-memchr_inv.c b/lib/test_fortify/read_overflow-memchr_inv.c
new file mode 100644 (file)
index 0000000..b26e1f1
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memchr_inv(small, 0x7A, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow-memcmp.c b/lib/test_fortify/read_overflow-memcmp.c
new file mode 100644 (file)
index 0000000..d5d301f
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memcmp(small, large, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow-memscan.c b/lib/test_fortify/read_overflow-memscan.c
new file mode 100644 (file)
index 0000000..c1a97f2
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memscan(small, 0x7A, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow2-memcmp.c b/lib/test_fortify/read_overflow2-memcmp.c
new file mode 100644 (file)
index 0000000..c6091e6
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memcmp(large, small, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow2-memcpy.c b/lib/test_fortify/read_overflow2-memcpy.c
new file mode 100644 (file)
index 0000000..07b62e5
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memcpy(large, instance.buf, sizeof(large))
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/read_overflow2-memmove.c b/lib/test_fortify/read_overflow2-memmove.c
new file mode 100644 (file)
index 0000000..34edfab
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memmove(large, instance.buf, sizeof(large))
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/test_fortify.h b/lib/test_fortify/test_fortify.h
new file mode 100644 (file)
index 0000000..d22664f
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+void do_fortify_tests(void);
+
+#define __BUF_SMALL    16
+#define __BUF_LARGE    32
+struct fortify_object {
+       int a;
+       char buf[__BUF_SMALL];
+       int c;
+};
+
+#define LITERAL_SMALL "AAAAAAAAAAAAAAA"
+#define LITERAL_LARGE "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+const char small_src[__BUF_SMALL] = LITERAL_SMALL;
+const char large_src[__BUF_LARGE] = LITERAL_LARGE;
+
+char small[__BUF_SMALL];
+char large[__BUF_LARGE];
+struct fortify_object instance;
+size_t size;
+
+void do_fortify_tests(void)
+{
+       /* Normal initializations. */
+       memset(&instance, 0x32, sizeof(instance));
+       memset(small, 0xA5, sizeof(small));
+       memset(large, 0x5A, sizeof(large));
+
+       TEST;
+}
diff --git a/lib/test_fortify/write_overflow-memcpy.c b/lib/test_fortify/write_overflow-memcpy.c
new file mode 100644 (file)
index 0000000..3b3984e
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memcpy(instance.buf, large_src, sizeof(large_src))
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-memmove.c b/lib/test_fortify/write_overflow-memmove.c
new file mode 100644 (file)
index 0000000..640437c
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memmove(instance.buf, large_src, sizeof(large_src))
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-memset.c b/lib/test_fortify/write_overflow-memset.c
new file mode 100644 (file)
index 0000000..36e3490
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       memset(instance.buf, 0x5A, sizeof(large_src))
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strcpy-lit.c b/lib/test_fortify/write_overflow-strcpy-lit.c
new file mode 100644 (file)
index 0000000..51effb3
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strcpy(small, LITERAL_LARGE)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strcpy.c b/lib/test_fortify/write_overflow-strcpy.c
new file mode 100644 (file)
index 0000000..84f1c56
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strcpy(small, large_src)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strlcpy-src.c b/lib/test_fortify/write_overflow-strlcpy-src.c
new file mode 100644 (file)
index 0000000..91bf83e
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strlcpy(small, large_src, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strlcpy.c b/lib/test_fortify/write_overflow-strlcpy.c
new file mode 100644 (file)
index 0000000..1883db7
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strlcpy(instance.buf, large_src, sizeof(instance.buf) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strncpy-src.c b/lib/test_fortify/write_overflow-strncpy-src.c
new file mode 100644 (file)
index 0000000..8dcfb8c
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strncpy(small, large_src, sizeof(small) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strncpy.c b/lib/test_fortify/write_overflow-strncpy.c
new file mode 100644 (file)
index 0000000..b85f079
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strncpy(instance.buf, large_src, sizeof(instance.buf) + 1)
+
+#include "test_fortify.h"
diff --git a/lib/test_fortify/write_overflow-strscpy.c b/lib/test_fortify/write_overflow-strscpy.c
new file mode 100644 (file)
index 0000000..38feddf
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define TEST   \
+       strscpy(instance.buf, large_src, sizeof(instance.buf) + 1)
+
+#include "test_fortify.h"
index 37d17a7..1a06585 100644 (file)
@@ -2486,9 +2486,7 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
        xdst = dst_alloc(dst_ops, NULL, 1, DST_OBSOLETE_NONE, 0);
 
        if (likely(xdst)) {
-               struct dst_entry *dst = &xdst->u.dst;
-
-               memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
+               memset_after(xdst, 0, u.dst);
        } else
                xdst = ERR_PTR(-ENOBUFS);
 
index 3a3cb09..7c36cc1 100644 (file)
@@ -2955,7 +2955,7 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
        copy_to_user_state(x, &ue->state);
        ue->hard = (c->data.hard != 0) ? 1 : 0;
        /* clear the padding bytes */
-       memset(&ue->hard + 1, 0, sizeof(*ue) - offsetofend(typeof(*ue), hard));
+       memset_after(ue, 0, hard);
 
        err = xfrm_mark_put(skb, &x->mark);
        if (err)
index cfcb607..5d54b57 100755 (executable)
@@ -1245,6 +1245,13 @@ sub dump_struct($$) {
        $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos;
        $members =~ s/\s*____cacheline_aligned_in_smp/ /gos;
        $members =~ s/\s*____cacheline_aligned/ /gos;
+       # unwrap struct_group():
+       # - first eat non-declaration parameters and rewrite for final match
+       # - then remove macro, outer parens, and trailing semicolon
+       $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos;
+       $members =~ s/\bstruct_group_(attr|tagged)\s*\(([^,]*,){2}/STRUCT_GROUP(/gos;
+       $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos;
+       $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos;
 
        my $args = qr{([^,)]+)};
        # replace DECLARE_BITMAP
@@ -1256,6 +1263,8 @@ sub dump_struct($$) {
        $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos;
        # replace DECLARE_KFIFO_PTR
        $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos;
+       # replace DECLARE_FLEX_ARRAY
+       $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos;
        my $declaration = $members;
 
        # Split nested struct/union elements as newer ones
diff --git a/scripts/test_fortify.sh b/scripts/test_fortify.sh
new file mode 100644 (file)
index 0000000..a4da365
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+set -e
+
+# Argument 1: Source file to build.
+IN="$1"
+shift
+# Extract just the filename for error messages below.
+FILE="${IN##*/}"
+# Extract the function name for error messages below.
+FUNC="${FILE#*-}"
+FUNC="${FUNC%%-*}"
+FUNC="${FUNC%%.*}"
+# Extract the symbol to test for in build/symbol test below.
+WANT="__${FILE%%-*}"
+
+# Argument 2: Where to write the build log.
+OUT="$1"
+shift
+TMP="${OUT}.tmp"
+
+# Argument 3: Path to "nm" tool.
+NM="$1"
+shift
+
+# Remaining arguments are: $(CC) $(c_flags)
+
+# Clean up temporary file at exit.
+__cleanup() {
+       rm -f "$TMP"
+}
+trap __cleanup EXIT
+
+# Function names in warnings are wrapped in backticks under UTF-8 locales.
+# Run the commands with LANG=C so that grep output will not change.
+export LANG=C
+
+status=
+# Attempt to build a source that is expected to fail with a specific warning.
+if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
+       # If the build succeeds, either the test has failed or the
+       # warning may only happen at link time (Clang). In that case,
+       # make sure the expected symbol is unresolved in the symbol list.
+       # If so, FORTIFY is working for this case.
+       if ! $NM -A "$OUT".o | grep -m1 "\bU ${WANT}$" >>"$TMP" ; then
+               status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
+       fi
+else
+       # If the build failed, check for the warning in the stderr (gcc).
+       if ! grep -q -m1 "error: call to .\b${WANT}\b." "$TMP" ; then
+               status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN"
+       fi
+fi
+
+if [ -n "$status" ]; then
+       # Report on failure results, including compilation warnings.
+       echo "$status" | tee "$OUT" >&2
+else
+       # Report on good results, and save any compilation output to log.
+       echo "ok: unsafe ${FUNC}() usage correctly detected with '$WANT' in $IN" >"$OUT"
+fi
+cat "$TMP" >>"$OUT"
index 0ced7fd..fe6c039 100644 (file)
@@ -191,6 +191,9 @@ config HARDENED_USERCOPY_PAGESPAN
 config FORTIFY_SOURCE
        bool "Harden common str/mem functions against buffer overflows"
        depends on ARCH_HAS_FORTIFY_SOURCE
+       # https://bugs.llvm.org/show_bug.cgi?id=50322
+       # https://bugs.llvm.org/show_bug.cgi?id=41459
+       depends on !CC_IS_CLANG
        help
          Detect overflows of buffers in common string and memory functions
          where the compiler can determine and validate the buffer sizes.