Bounds Checker Report — 20260820_150314_block

Date: 2026-08-20 15:04:37 Model: claude-sonnet-4-6 Source: block Files: 98 Stage 1: 68 findings (Cat A: 2  |  Cat B: 5  |  Cat C: 7  |  Cat E: 37  |  Cat F: 12  |  Cat G2: 3  |  Cat H: 2) Stage 2: 1 real  |  67 FP  |  0 unanalyzed Kernel: v7.2-rc7-32-gaf5a019bf97e Branch: work/fscache-fixes
32 local commit(s) since origin/master
  1. af5a019bf97e cifs: fix i_size inconsistency in smb2_duplicate_extents() on FSCTL failure
  2. e6a9c11309cd cifs: call pagecache_isize_extended() in cifs_setsize() when extending
  3. 5fce3864d8e5 smb: client: restore the data_offset bound in is_valid_oplock_break()
  4. 7f11fbcc7141 cifs: clear tcon after cifsFileInfo_put() in cifs_file_set_size()
  5. 1e13b07759b9 smb: client: Avoid leaking sensitive data to the heap in connect.c
  6. d91fefcf00b0 smb: client: Clear sensitive stack data in smb1encrypt.c
  7. 24ec30094b95 smb: client: Clear sensitive stack data in cifsencrypt.c
  8. 3e200e7995cc smb: client: Clear sensitive stack and heap data in smb2ops.c
  9. 13881ad0a8d9 smb: client: Clear sensitive stack data in smb2transport.c
  10. 3356da3ae12b Revert "cifs: remove all cifs files before kill super"
  11. 98dfc0f0348c smb: client: fix use-before-check of ReparseDataLength in reparse_buf_ptr()
  12. 442beae405ea smb: client: fix ALIGN() overflow in symlink_data() error context loop
  13. 67b83682e9c5 smb: client: simplify __build_path_from_dentry_optional_prefix()
  14. cb4c026ddc77 smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2
  15. f1e640a65f45 smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2()
  16. e56686589295 smb/client: decode reparse metadata using its payload type
  17. 0a09b6270c91 smb/client: preserve open info type across compound queries
  18. c2f17b3a404d smb/client: mark missing nlink values as unknown
  19. 746f8bb16e15 cifs: fix clearing stats for fastest execution of each smb2 command
  20. 7886f8ae3173 smb/client: remove unused file flags ioctl handlers
  21. 1f852aaa3c8c smb/client: implement fileattr_set for compression flags
  22. 77d852c76342 smb/client: fix nlink of an overwritten open file
  23. 60be95527bc8 cifs: remove dead size-update blocks in cifs_setattr_unix/nounix
  24. 297d8026a570 cifs: remove redundant size-update block in cifs_remap_file_range()
  25. 32a7af68df73 cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem
  26. 364b18323058 cifs: use cifs_invalidate_cache() in cifs_do_truncate() for O_TRUNC
  27. b1b741cf8e7c smb: client: clear ce->tgthint in free_tgts()
  28. bf86c08123c6 smb: client: harden DFS cache against invalid target hints
  29. b8e5dc4f95e5 smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions
  30. 45f84cf25a08 smb: client: set replay flag on the read send-error retry path
  31. deb6468f4164 smb: client: fix request buffer leak in smb2_new_read_req()
  32. a2f9fb451c68 smb/client: return EOPNOTSUPP for unsupported O_TMPFILE

Contents

block/ (4 functions) — 0 real, 5 FP
block/partitions/ (13 functions) — 1 real, 62 FP
block/ (4 functions) — 0 real, 5 FP

Summary

FunctionFileAssessmentConfidenceRealFPUnanalyzed
block/ — 4 functions, 0 real, 5 FP
bsg_transport_sg_io_fn()block/bsg-lib.cFPhigh010
blkdev_pr_read_keys()block/ioctl.cFPhigh010
block/partitions/ — 13 functions, 1 real, 62 FP
aix_partition()block/partitions/aix.cFPhigh0160
amiga_partition()block/partitions/amiga.cFPhigh010
alloc_read_gpt_entries()block/partitions/efi.cFPhigh020
efi_partition()block/partitions/efi.cFPhigh010
find_valid_gpt()block/partitions/efi.cFPhigh010
utf16_le_to_7bit()block/partitions/efi.cFPhigh010
ldm_frag_add()block/partitions/ldm.cMIXEDhigh1140
mac_partition()block/partitions/mac.cFPhigh0210
parse_bsd()block/partitions/msdos.cFPhigh010
parse_solaris_x86()block/partitions/msdos.cFPhigh010
osf_partition()block/partitions/osf.cFPhigh010
sun_partition()block/partitions/sun.cFPhigh010
sysv68_partition()block/partitions/sysv68.cFPhigh010
block/ — 4 functions, 0 real, 5 FP
opal_discovery0_end()block/sed-opal.cFPhigh020
response_parse()block/sed-opal.cFPmedium010

Function Details

bsg_transport_sg_io_fn() — block/bsg-lib.c FP confidence=high

The function correctly uses min() to bound the copy size against the kernel-internal job->reply_len before calling copy_to_user(). hdr->max_response_len is user-supplied but is constrained by the kernel-controlled reply buffer length. The destination of copy_to_user is user space, so there is no kernel buffer to overflow.

Finding #1 — Category G2 — false positive

CategoryCat G2 — unvalidated size argument to copy_from/to_user
Taint sourcecopy_to_user() line 118
Taint snippetif (copy_to_user(uptr64(hdr->response), job->reply, len))
Tainted varlen
Unvalidated sizecopy_to_user() arg 2 line 118 — size len
Sink snippetif (copy_to_user(uptr64(hdr->response), job->reply, len))
Possibly guardedno
Dismissed: len = min(hdr->max_response_len, job->reply_len). job->reply_len is set to SCSI_SENSE_BUFFERSIZE (kernel constant) or sizeof(u32) by kernel code — not user-controlled. The min() ensures len never exceeds the actual reply buffer size. Counterexample attempt: hdr->max_response_len=UINT_MAX, job->reply_len=96 yields len=96, which is exactly the buffer size — no OOB. copy_to_user writes to user space, so there is no fixed-size kernel destination buffer that could be overflowed. Finding is a false positive.

blkdev_pr_read_keys() — block/ioctl.c FP confidence=high

The function properly validates the user-supplied num_keys against PR_KEYS_MAX before allocation, and uses min() to cap the copy length. The keys_copy_len is bounded by the validated read_keys.num_keys, which equals the number of entries allocated in keys_info->keys, making the copy safe from the kernel buffer side. The destination is a user pointer, which is appropriate for copy_to_user.

Finding #1 — Category G2 — false positive

CategoryCat G2 — unvalidated size argument to copy_from/to_user
Taint sourcecopy_to_user() line 470
Taint snippetif (copy_to_user(keys_ptr, keys_info->keys, keys_copy_len)) {
Tainted varkeys_copy_len
Unvalidated sizecopy_to_user() arg 2 line 470 — size keys_copy_len
Sink snippetif (copy_to_user(keys_ptr, keys_info->keys, keys_copy_len)) {
Possibly guardedno
Dismissed: keys_copy_len = min(read_keys.num_keys, keys_info->num_keys) * sizeof(...). Since read_keys.num_keys was validated to be <= PR_KEYS_MAX before allocation, and keys_info was allocated for exactly read_keys.num_keys entries, the min() ensures we never read beyond the allocated buffer. No counterexample can be constructed: any value of keys_info->num_keys larger than read_keys.num_keys would be capped by min(), and read_keys.num_keys <= PR_KEYS_MAX was checked at line 450. The scanner flagged this because the size involves a user-supplied field, but the combination of the upper-bound check and min() makes it safe.

aix_partition() — block/partitions/aix.c FP confidence=high

The code has reasonable validation: (1) the numlvs loop has two additional upper-bound guards (state->limit and sector size); (2) pvd is a locally kernel-allocated struct of known fixed size, not a raw server-supplied pointer; (3) numpps is clamped to ARRAY_SIZE(pvd->ppe) before the loop; (4) lv_ix has an explicit bounds check before all uses; (5) put_partition() has its own internal bounds check. The scanner incorrectly propagated taint through alloc_pvd() treating the returned kernel allocation as a tainted offset.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe16_to_cpu() line 197
Taint snippetnumlvs = be16_to_cpu(p->numlvs);
Tainted varnumlvs
Loopfor_loop line 217
Sink snippetfor (i = 0; foundlvs < numlvs && i < state->limit &&
Possibly guardedno
Dismissed: Loop has two additional guards: i < state->limit (bounds lvip array) and i < SECTOR_SIZE/sizeof(struct lvd) (bounds sector buffer). numlvs being arbitrarily large just causes early termination. No counterexample possible: to go OOB on lvip, i would need >= state->limit, but that guard fires first.

Finding #2 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varpvd
Pointer derefpvd->pp_count line 229
Sink snippetint numpps = be16_to_cpu(pvd->pp_count);
Possibly guardedno
Dismissed: pvd is NOT a pointer into a raw server buffer. alloc_pvd() calls kmalloc(sizeof(struct pvd)) then reads exactly sizeof(struct pvd) bytes into it, returning NULL on short reads. pvd->pp_count is a field within a fully kernel-allocated struct. Scanner incorrectly propagated taint from vgda_sector through alloc_pvd().

Finding #3 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varpvd
Pointer derefpvd->psn_part1 line 230
Sink snippetint psn_part1 = be32_to_cpu(pvd->psn_part1);
Possibly guardedno
Dismissed: Same as #2: pvd->psn_part1 is a field in a locally allocated struct pvd of known size. False positive from incorrect taint propagation through alloc_pvd().

Finding #4 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varpvd
Pointer derefpvd->ppe line 242
Sink snippetif (numpps > ARRAY_SIZE(pvd->ppe))
Possibly guardedno
Dismissed: pvd->ppe is an array field within the locally allocated struct pvd. ARRAY_SIZE(pvd->ppe) is a compile-time constant. No OOB possible.

Finding #5 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varpvd
Pointer derefpvd->ppe line 243
Sink snippetnumpps = ARRAY_SIZE(pvd->ppe);
Possibly guardedyes (heuristic)
Dismissed: Same as #4. pvd is locally allocated; pvd->ppe assignment is within the fixed-size struct.

Finding #6 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varnumpps
Loopfor_loop line 245
Sink snippetfor (i = 0; i < numpps; i += 1) {
Possibly guardedno
Dismissed: numpps is clamped to ARRAY_SIZE(pvd->ppe) at lines 242-243 before the loop. The loop index i runs from 0 to numpps-1, accessing pvd->ppe[i] which is within the allocated struct. No counterexample: numpps cannot exceed ARRAY_SIZE(pvd->ppe) due to explicit clamp.

Finding #7 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varpvd
Pointer derefpvd->ppe line 246
Sink snippetstruct ppe *p = pvd->ppe + i;
Possibly guardedno
Dismissed: p = pvd->ppe + i where i < numpps <= ARRAY_SIZE(pvd->ppe). Both pvd and the ppe array are within the locally kmalloc'd struct pvd. Safe.

Finding #8 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varp
Pointer derefp->lp_ix line 249
Sink snippetlp_ix = be16_to_cpu(p->lp_ix);
Possibly guardedno
Dismissed: p points into pvd->ppe[i] which is within the locally allocated struct pvd of known size. p->lp_ix is a field access within a locally kmalloc'd struct. The taint chain through vgda_sector -> alloc_pvd -> pvd -> ppe+i -> p->lp_ix is all within the kernel-allocated struct.

Finding #9 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe32_to_cpu() line 186
Taint snippetvgda_sector = be32_to_cpu(p->vgda_psn[0]);
Tainted varp
Pointer derefp->lv_ix line 254
Sink snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Possibly guardedno
Dismissed: Same as #8. p->lv_ix is within the locally allocated struct pvd. False positive.

Finding #10 — Category C — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Subscript[] line 259
Sink snippetlvip[lv_ix].pps_found += 1;
Possibly guardedyes (heuristic)
Dismissed: lv_ix = be16_to_cpu(p->lv_ix) - 1. If p->lv_ix == 0, lv_ix wraps to UINT_MAX (unsigned), which fails the check at line 255 (lv_ix >= state->limit) and continues. For any nonzero value, lv_ix is checked against state->limit before use. lvip has state->limit entries. No counterexample possible.

Finding #11 — Category C — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Subscript[] line 267
Sink snippetif (lp_ix == lvip[lv_ix].pps_per_lv) {
Possibly guardedyes (heuristic)
Dismissed: Same guard as #10 applies. lv_ix < state->limit guaranteed at this point. lvip[lv_ix] is within bounds.

Finding #12 — Category C — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Subscript[] line 270
Sink snippetlvip[lv_ix].pps_per_lv * pp_blocks_size);
Possibly guardedyes (heuristic)
Dismissed: Same guard applies. lv_ix < state->limit at this code path. False positive.

Finding #13 — Category C — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Subscript[] line 272
Sink snippetn[lv_ix].name);
Possibly guardedyes (heuristic)
Dismissed: n[lv_ix].name: lv_ix < state->limit. n was allocated by alloc_lvn() with LVM_MAXLVS entries. If state->limit > LVM_MAXLVS, there could be an issue, but typically state->limit is bounded. The existing guard on lv_ix is necessary and appears sufficient assuming state->limit <= LVM_MAXLVS.

Finding #14 — Category C — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Subscript[] line 273
Sink snippetlvip[lv_ix].lv_is_contiguous = 1;
Possibly guardedyes (heuristic)
Dismissed: Same as #10-#12. lv_ix < state->limit is guaranteed by the guard at line 255.

Finding #15 — Category C — cross-function via put_partition() — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Call siteline 268 — passes lv_ix to put_partition()
Call snippetput_partition(state, lv_ix + 1,
Subscript (in callee)[] line 41
Sink snippetp->parts[n].from = from;
Possibly guardedyes (heuristic)
Dismissed: put_partition() is called with lv_ix+1. put_partition() itself checks n < p->limit before indexing p->parts[n]. Additionally, lv_ix < state->limit = p->limit, so lv_ix+1 <= state->limit, and the callee's guard (n < p->limit) ensures safety.

Finding #16 — Category C — cross-function via put_partition() — false positive

CategoryCat C — server value → array subscript
Taint sourcebe16_to_cpu() line 254
Taint snippetlv_ix = be16_to_cpu(p->lv_ix) - 1;
Tainted varlv_ix
Call siteline 268 — passes lv_ix to put_partition()
Call snippetput_partition(state, lv_ix + 1,
Subscript (in callee)[] line 42
Sink snippetp->parts[n].size = size;
Possibly guardedyes (heuristic)
Dismissed: Same as #15. put_partition() validates n < p->limit before both parts[n].from and parts[n].size assignments.

amiga_partition() — block/partitions/amiga.c FP confidence=high

The value 'blk' from rdb->rdb_PartitionList is indeed disk-supplied (server-supplied in the broader sense), but it does NOT control an iteration count in the traditional sense. The loop termination condition is 'part<=16' (a hard cap of 16 iterations) AND '(s32)blk>0'. Each iteration, 'blk' is updated to 'pb->pb_Next' (another disk-supplied value). The loop is essentially a linked-list traversal, not an array traversal indexed by 'blk'. The 'blk' value is used as a sector number to read from disk via read_part_sector(), which itself handles invalid sector numbers safely (returning NULL, which triggers an error return). There is no buffer whose bounds could be violated by the value of 'blk': it's passed to read_part_sector() which validates access to the disk, and if the read fails, the code returns -1. The hard cap of part<=16 prevents infinite linked-list traversal. The overflow check at line 90 (check_mul_overflow) prevents arithmetic overflow when computing the actual disk block number. This is a false positive — the scanner misidentified a disk sector address (used as an argument to a safe I/O function with NULL-check) as an unchecked loop bound.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe32_to_cpu() line 86
Taint snippetblk = be32_to_cpu(rdb->rdb_PartitionList);
Tainted varblk
Loopfor_loop line 88
Sink snippetfor (part = 1; (s32) blk>0 && part<=16; part++, put_dev_sector(sect)) {
Possibly guardedno
Dismissed: The loop has two independent termination guards: (1) a hard cap 'part<=16' limiting to at most 16 iterations regardless of disk content, and (2) '(s32)blk>0' which stops if the disk-supplied next-block pointer is non-positive. Within each iteration, 'blk' is multiplied by 'blksize' with overflow checking (check_mul_overflow at line 90), then passed to read_part_sector() which returns NULL on failure (invalid sector) causing a clean error exit. There is no array indexing or memory access where 'blk' could cause OOB — it is only used as a disk sector address for I/O. No counterexample can be constructed where a specific value of 'blk' causes memory corruption, because read_part_sector() handles all sector addresses (valid or not) and returns NULL on error. The finding is a false positive.

alloc_read_gpt_entries() — block/partitions/efi.c FP confidence=high

alloc_read_gpt_entries() is a static function called only from is_gpt_valid(). Before the call at line 429, is_gpt_valid() validates: (1) sizeof_partition_entry == sizeof(gpt_entry) exactly (line 415), and (2) num_partition_entries * sizeof_partition_entry <= KMALLOC_MAX_SIZE (lines 421-427). These two gates together bound 'count' to a safe kmalloc size and ensure the allocation is not undersized relative to the data read. Inside read_lba(), memcpy uses 'copied' which is min(512, remaining_count), writing into a buffer of exactly 'count' bytes, so no OOB write is possible.

Finding #1 — Category B — false positive

CategoryCat B — server value → size/alloc argument
Taint sourcele32_to_cpu() line 279
Taint snippetcount = (size_t)le32_to_cpu(gpt->num_partition_entries) *
Tainted varcount
Sinkkmalloc() line 283 (arg 0, role=size)
Sink snippetpte = kmalloc(count, GFP_KERNEL);
Possibly guardedno
Dismissed: The call site (is_gpt_valid) validates pt_size <= KMALLOC_MAX_SIZE before calling alloc_read_gpt_entries(). Since alloc_read_gpt_entries() is static and only called from this one validated call site, 'count' is bounded. No counterexample can be constructed: any value that would overflow or exceed KMALLOC_MAX_SIZE is rejected by the gate at lines 421-427.

Finding #2 — Category B — cross-function via read_lba() — false positive

CategoryCat B — server value → size/alloc argument
Taint sourcele32_to_cpu() line 279
Taint snippetcount = (size_t)le32_to_cpu(gpt->num_partition_entries) *
Tainted varcount
Call siteline 287 — passes count to read_lba()
Call snippetif (read_lba(state, le64_to_cpu(gpt->partition_entry_lba),
Sink (in callee)memcpy() line 252 (arg 2, role=size)
Sink snippetmemcpy(buffer, data, copied);
Possibly guardedno
Dismissed: In read_lba(), the memcpy size argument is 'copied' = min(512, remaining_count), not 'count' directly. The destination buffer 'pte' was allocated with kmalloc(count), and the loop reads exactly up to 'count' total bytes in 512-byte chunks, so no OOB write is possible. The cross-function taint path is safe because the destination is sized to 'count' and the per-iteration copy is bounded by remaining capacity.

efi_partition() — block/partitions/efi.c FP confidence=high

The GPT header is read from disk (server/storage-supplied), so num_partition_entries is externally supplied. However, find_valid_gpt() is called before the loop and its postcondition explicitly states that *gpt is a validated GPT header and *ptes is a validated array of partition entries verified against disk sector boundaries and GPT structural constraints. This validation covers the loop bound: find_valid_gpt() would have returned 0 if num_partition_entries were inconsistent with the actual allocated ptes array. Furthermore, the loop condition has a double guard: `i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1`, where state->limit is the kernel's own limit on the number of partitions. Any valid i that satisfies both conditions is safe to index into ptes[i] (validated by find_valid_gpt) and state->parts[i+1] (bounded by state->limit-1 check). No counterexample can be constructed that passes find_valid_gpt() validation AND the state->limit guard AND still causes OOB access.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcele32_to_cpu() line 727
Taint snippetfor (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) {
Tainted var(inline)
Loopfor_loop line 727
Sink snippetfor (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) {
Possibly guardedno
Dismissed: find_valid_gpt() [called before taint source] establishes that gpt->num_partition_entries is consistent with the allocated ptes array — this is a full prior-traversal/structural validation. The additional `i < state->limit-1` guard in the loop condition independently caps iteration to the kernel's own partition table size. To construct a counterexample, num_partition_entries would need to exceed the actual number of valid ptes entries, but find_valid_gpt() rejects that case. No counterexample exists that bypasses both guards; the finding is a false positive.

find_valid_gpt() — block/partitions/efi.c FP confidence=high

find_valid_gpt() uses is_gpt_valid() as a comprehensive validator that checks GPT header integrity, CRC, LBA bounds, and partition entry array validity before returning. The function's validation discipline is sound. The scanner misidentified the flow: le64_to_cpu(pgpt->alternate_lba) is passed as a sector_t argument to is_gpt_valid(), which validates it internally; good_agpt receives only the int return value (0 or 1) of that function, not the 64-bit sector value. No actual truncation occurs.

Finding #1 — Category H — false positive

CategoryCat H — server value → narrow integer type (silent truncation)
Taint sourcele64_to_cpu() line 617
Taint snippetgood_agpt = is_gpt_valid(state,
Tainted vargood_agpt
Truncationline 617: 64 → 32-bit u32
Sink snippetgood_agpt = is_gpt_valid(state,
Possibly guardedno
Dismissed: The scanner incorrectly identifies good_agpt as carrying a 64-bit truncation. good_agpt is an int receiving the return value of is_gpt_valid() (always 0 or 1). The le64_to_cpu(pgpt->alternate_lba) u64 value is passed as a sector_t parameter to is_gpt_valid(), which validates it against disk bounds internally per its documented postconditions. No 64→32 bit truncation of the sector value into good_agpt actually occurs. Counterexample: no concrete values of alternate_lba can cause good_agpt to be a truncated large number, since good_agpt only ever holds 0 or 1 from is_gpt_valid()'s return.

utf16_le_to_7bit() — block/partitions/efi.c FP confidence=high

The scanner flagged a 16-bit to 8-bit truncation, but the RHS expression explicitly applies '& 0x7f' before assignment to the u8 variable. This masking limits the value to 7 bits (0–127), which trivially fits in a u8 (0–255). The scanner's own note says to flag as false positive when masking limits the value to the destination width — that condition is met here. Additionally, the 'partition_name' field comes from a GPT partition table on disk (device-supplied, not network server-supplied in the strict sense), but even treating it as untrusted input, the masking makes the truncation safe. The call site also correctly caps 'label_max' using min() of the two array sizes, preventing any OOB access.

Finding #1 — Category H — false positive

CategoryCat H — server value → narrow integer type (silent truncation)
Taint sourcele16_to_cpu() line 684
Taint snippetu8 c = le16_to_cpu(in[i]) & 0x7f;
Tainted varc
Truncationline 684: 16 → 8-bit u8
Sink snippetu8 c = le16_to_cpu(in[i]) & 0x7f;
Possibly guardedno
Dismissed: The expression 'le16_to_cpu(in[i]) & 0x7f' explicitly masks the 16-bit value to 7 bits before assigning to u8 c. The scanner's own guideline states this is a false positive when masking limits the value to the destination width. No counterexample exists: any 16-bit value AND-ed with 0x7f produces a value in [0, 127], which is always within u8 range. The call site uses min(ARRAY_SIZE(info->volname)-1, ARRAY_SIZE(ptes[i].partition_name)) to bound 'label_max', ensuring neither the read of in[i] nor the write to out[i] can go out of bounds.

ldm_frag_add() — block/partitions/ldm.c MIXED confidence=high

Most findings (#2-#15) are false positives: the scanner incorrectly propagated taint from the server-supplied 'num' variable through the kmalloc() return value 'f', treating all subsequent accesses to the locally-allocated struct as tainted pointer dereferences. In reality, 'f' is a kernel-allocated buffer and field accesses are writes to local memory, not reads from untrusted data. Finding #1 is a genuine integer overflow risk: 'size*num' in the kmalloc argument lacks overflow protection. Although 'num' is bounded to [1,4], 'size' (ldb->vm.vblk_size) is disk-supplied and may not be sufficiently validated, allowing size*num to wrap on 32-bit arithmetic.

Finding #1 — Category B — INTEGER OVERFLOW — BUG undersized_alloc

CategoryCat B — integer overflow: sizeof (*f) + size*num
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Overflow exprsizeof (*f) + size*num
Safe fixkmalloc_array() or check_mul_overflow()
Sinkkmalloc() line 1247 (arg 0, role=size_mul_overflow)
Sink snippetf = kmalloc (sizeof (*f) + size*num, GFP_KERNEL);
Possibly guardedyes (heuristic)
Server-suppliedyes
Check presentyes
Check sufficientno
Symptom: BUG: KASAN: slab-out-of-bounds in ldm_frag_add or subsequent heap corruption if size*num wraps to a small value, causing kmalloc to return an undersized buffer
Fix: Replace 'kmalloc(sizeof(*f) + size*num, GFP_KERNEL)' with a safe form: check_mul_overflow(size, num, &total) || check_add_overflow(sizeof(*f), total, &alloc_size), or use kmalloc(struct_size(f, data, (size_t)size * num), GFP_KERNEL) with prior overflow check. Also validate 'size' against a reasonable maximum before this computation.
CVE pattern: integer overflow in kmalloc size expression leading to undersized allocation

Finding #2 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->group line 1253
Sink snippetf->group = group;
Possibly guardedyes (heuristic)
Dismissed: False positive. 'f' is a pointer returned by kmalloc(), locally allocated. The scanner incorrectly propagated taint from 'num' (used in the allocation size) to 'f' itself. f->group is a write to kernel-allocated memory, not an access to server-supplied data.

Finding #3 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->num line 1254
Sink snippetf->num = num;
Possibly guardedyes (heuristic)
Dismissed: False positive. Same reasoning as #2: 'f' is locally kmalloc'd; f->num is a write to local memory.

Finding #4 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->rec line 1255
Sink snippetf->rec = rec;
Possibly guardedyes (heuristic)
Dismissed: False positive. 'f' is locally kmalloc'd; f->rec is a write to local memory.

Finding #5 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->map line 1256
Sink snippetf->map = 0xFF << num;
Possibly guardedyes (heuristic)
Dismissed: False positive. 'f' is locally kmalloc'd; f->map = 0xFF << num. Note: 'num' bounded [1,4] so shift is safe (no UB). Access to f->map is to local memory.

Finding #6 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->list line 1258
Sink snippetlist_add_tail (&f->list, frags);
Possibly guardedyes (heuristic)
Dismissed: False positive. 'f' is locally kmalloc'd; list_add_tail operates on locally allocated memory.

Finding #7 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->num line 1260
Sink snippetif (rec >= f->num) {
Possibly guardedyes (heuristic)
Dismissed: False positive. At the 'found:' label, 'f' may come from a list traversal (existing frag) or from kmalloc. In either case it's a kernel-managed struct. f->num is a read from a previously kernel-written field.

Finding #8 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->num line 1261
Sink snippetldm_error("REC value (%d) exceeds NUM value (%d)", rec, f->num);
Possibly guardedyes (heuristic)
Dismissed: False positive. Same as #7; f->num in the error message is a read of a kernel-written field.

Finding #9 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->map line 1264
Sink snippetif (f->map & (1 << rec)) {
Possibly guardedyes (heuristic)
Dismissed: False positive. f->map is a kernel-written field; the access is to local/kernel-managed memory.

Finding #10 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->map line 1266
Sink snippetf->map &= 0x7F; /* Mark the group as broken */
Possibly guardedyes (heuristic)
Dismissed: False positive. f->map &= 0x7F is a write to kernel-allocated memory.

Finding #11 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->map line 1269
Sink snippetf->map |= (1 << rec);
Possibly guardedyes (heuristic)
Dismissed: False positive. f->map |= (1 << rec) is a write to kernel-allocated memory. 'rec' is bounded by rec < num <= 4 so the shift is safe.

Finding #12 — Category A — false positive

CategoryCat A — server offset → pointer → memory op
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Sinkmemcpy() line 1271 (arg 0, role=pointer)
Sink snippetmemcpy(f->data, data, VBLK_SIZE_HEAD);
Possibly guardedyes (heuristic)
Dismissed: False positive. f->data is the flexible array member of the locally kmalloc'd struct. The buffer was allocated with sizeof(*f)+size*num bytes. VBLK_SIZE_HEAD fits within that allocation (size >= 2*VBLK_SIZE_HEAD per the guard at line 1224). The destination is kernel-allocated memory.

Finding #13 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->data line 1271
Sink snippetmemcpy(f->data, data, VBLK_SIZE_HEAD);
Possibly guardedyes (heuristic)
Dismissed: False positive. Same as #12; f->data dereference is to locally allocated memory.

Finding #14 — Category A — false positive

CategoryCat A — server offset → pointer → memory op
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Sinkmemcpy() line 1274 (arg 0, role=pointer)
Sink snippetmemcpy(f->data + VBLK_SIZE_HEAD + rec * size, data, size);
Possibly guardedyes (heuristic)
Dismissed: False positive. The destination f->data + VBLK_SIZE_HEAD + rec*size: rec < num and the buffer is sized sizeof(*f)+size*num, so rec*size < num*size fits within the allocation. The source pointer is kernel-allocated memory.

Finding #15 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourceget_unaligned_be16() line 1231
Taint snippetnum = get_unaligned_be16(data + 0x0E);
Tainted varf
Pointer dereff->data line 1274
Sink snippetmemcpy(f->data + VBLK_SIZE_HEAD + rec * size, data, size);
Possibly guardedyes (heuristic)
Dismissed: False positive. Same as #14; f->data is locally allocated memory, not a server-supplied pointer.

mac_partition() — block/partitions/mac.c FP confidence=high

The function has careful validation that is sufficient. secsize must be a power of 2 (enforced at line 64). If secsize < 512, datasize=round_down(secsize,512)=0, and the check at line 71 always fails (returning -1), so the loop is never reached. If secsize >= 512, then secsize is a multiple of 512 (power of 2 ≥ 512), so pos%512 = (slot*secsize)%512 = 0 always, meaning part always points to data+0, well within any sector. blocks_in_map is validated against DISK_MAX_PARTS and state->limit. The 'l' variable from strnlen is bounded by sizeof(part->name), making the inner loop safe. | The function validates secsize as a power-of-2, and the check at line 71 (partoffset + sizeof(*part) > datasize) rejects secsize < 512 before the loop. For secsize >= 512 (power of 2), pos%512 is always 0, so 'part' always points to data+0, well within the sector buffer. The struct mac_partition fields (including ->name) are thus safely accessible.

Finding #1 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->signature line 76
Sink snippetif (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) {
Possibly guardedyes (heuristic)
Dismissed: Line 71 checks partoffset + sizeof(*part) <= datasize before creating the part pointer. No counterexample possible: if secsize < 512, datasize=0 and check fails; if secsize >= 512, partoffset=0 and datasize=secsize >= 512 >= sizeof(*part).

Finding #2 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->map_count line 80
Sink snippetblocks_in_map = be32_to_cpu(part->map_count);
Possibly guardedyes (heuristic)
Dismissed: Same protection as finding #1 — the line 71 guard ensures part is safely dereferenceable.

Finding #3 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe32_to_cpu() line 80
Taint snippetblocks_in_map = be32_to_cpu(part->map_count);
Tainted varblocks_in_map
Loopfor_loop line 90
Sink snippetfor (slot = 1; slot <= blocks_in_map; ++slot) {
Possibly guardedyes (heuristic)
Dismissed: blocks_in_map is checked: (1) against 0 and DISK_MAX_PARTS at line 81-84; (2) clamped to state->limit-1 at line 86-87. Each iteration reads a fresh sector via read_part_sector() and checks the magic number, breaking on mismatch. The loop count is properly bounded.

Finding #4 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->signature line 97
Sink snippetif (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
Possibly guardedyes (heuristic)
Dismissed: In the loop: pos = slot*secsize; secsize is power-of-2 >= 512 (guaranteed by line 71 gate), so pos%512 = 0 always. part = data + 0, which fits within the 512-byte sector returned by read_part_sector. No counterexample possible.

Finding #5 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->start_block line 100
Sink snippetbe32_to_cpu(part->start_block) * (secsize/512),
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4 — part is always at data+0 with secsize a multiple of 512, so all struct fields are within the sector.

Finding #6 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->block_count line 101
Sink snippetbe32_to_cpu(part->block_count) * (secsize/512));
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #7 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->type line 103
Sink snippetif (!strncasecmp(part->type, "Linux_RAID", 10))
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #8 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->processor line 113
Sink snippetmac_fix_string(part->processor, 16);
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4. Additionally these accesses are under CONFIG_PPC_PMAC and only within the loop after magic check.

Finding #9 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->name line 114
Sink snippetmac_fix_string(part->name, 32);
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #10 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->type line 115
Sink snippetmac_fix_string(part->type, 32);
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #11 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->status line 117
Sink snippetif ((be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE)
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #12 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->processor line 118
Sink snippet&& strcasecmp(part->processor, "powerpc") == 0)
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #13 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->type line 121
Sink snippetif (strcasecmp(part->type, "Apple_UNIX_SVR2") == 0
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #14 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->type line 122
Sink snippet|| (strncasecmp(part->type, "Linux", 5) == 0
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #15 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->type line 123
Sink snippet&& strcasecmp(part->type, "Linux_swap") != 0)) {
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4.

Finding #16 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->name line 127
Sink snippetl = strnlen(part->name, sizeof(part->name));
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4. strnlen with sizeof(part->name) bound keeps access within the struct.

Finding #17 — Category B — false positive

CategoryCat B — server value → size/alloc argument
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Sinkstrncmp() line 128 (arg 2, role=size)
Sink snippetif (strncmp(part->name, "/", sizeof(part->name)) == 0)
Possibly guardedyes (heuristic)
Dismissed: sizeof(part->name) is a compile-time constant, not derived from secsize. The size argument to strncmp is sizeof(part->name), which is completely safe — it bounds both the read of part->name and the comparison length. The scanner incorrectly traced secsize taint through to this sizeof expression.

Finding #18 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->name line 128
Sink snippetif (strncmp(part->name, "/", sizeof(part->name)) == 0)
Possibly guardedyes (heuristic)
Dismissed: Same as finding #4 for the pointer safety. The strncmp uses sizeof(part->name) as limit.

Finding #19 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varl
Loopfor_loop line 130
Sink snippetfor (i = 0; i <= l - 4; ++i) {
Possibly guardedno
Dismissed: l = strnlen(part->name, sizeof(part->name)). The result l is bounded by sizeof(part->name) — a compile-time constant — regardless of disk content. The loop 'i <= l-4' thus iterates at most sizeof(part->name)-4 times, always accessing within part->name's fixed-size array. No OOB possible.

Finding #20 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->name line 131
Sink snippetif (strncasecmp(part->name + i, "root",
Possibly guardedyes (heuristic)
Dismissed: part->name + i where i <= l-4 and l <= sizeof(part->name), so i+4 <= sizeof(part->name). The access strncasecmp(part->name+i, "root", 4) reads exactly 4 bytes within the name array bounds. No OOB possible.

Finding #21 — Category E — false positive

CategoryCat E — tainted pointer dereference (->field access beyond packet bounds)
Taint sourcebe16_to_cpu() line 54
Taint snippetsecsize = be16_to_cpu(md->block_size);
Tainted varpart
Pointer derefpart->name line 137
Sink snippetif (strncasecmp(part->name, "swap", 4) == 0)
Possibly guardedyes (heuristic)
Dismissed: secsize must be a power of 2 (line 64 check). The early return at lines 71-74 rejects secsize < 512 (since datasize = round_down(secsize,512) = 0 for secsize < 512, making partoffset+sizeof(*part) > 0 true). For secsize >= 512, pos = slot*secsize is always a multiple of 512, so pos%512 == 0 and part = data with no offset, safely within the page buffer. No counterexample exists that passes all guards yet causes OOB.

parse_bsd() — block/partitions/msdos.c FP confidence=high

parse_bsd() receives 'max_partitions' from callers always set to BSD_MAXPARTITIONS. Line 364-365 only reduces max_partitions (never increases it) to min(BSD_MAXPARTITIONS, d_npartitions). Since d_partitions[] is a fixed array of BSD_MAXPARTITIONS elements in struct bsd_disklabel, the loop bound is always within the array. The d_magic check at line 357 further validates the disk label structure before use.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcele16_to_cpu() line 365
Taint snippetmax_partitions = le16_to_cpu(l->d_npartitions);
Tainted varmax_partitions
Loopfor_loop line 366
Sink snippetfor (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
Possibly guardedno
Dismissed: All three call sites pass BSD_MAXPARTITIONS as max_partitions. Line 364-365 sets max_partitions = min(max_partitions, d_npartitions), which can only equal or decrease the value — never increase it beyond BSD_MAXPARTITIONS. The d_partitions array in struct bsd_disklabel is statically sized BSD_MAXPARTITIONS elements, so the loop bound max_partitions <= BSD_MAXPARTITIONS is always within array bounds. No counterexample can be constructed: any value of d_npartitions either reduces max_partitions (safe) or leaves it at BSD_MAXPARTITIONS (exactly the array bound). False positive.

parse_solaris_x86() — block/partitions/msdos.c FP confidence=high

The function reads v_nparts from a disk sector (server/disk-supplied), but immediately clamps it through a ternary expression to either SOLARIS_X86_NUMSLICE (16) or 8 — both compile-time constants. The loop bound max_nparts is therefore always one of two known safe values, and v_slice[] has SOLARIS_X86_NUMSLICE entries. No OOB access is possible. The taint tracker flagged le16_to_cpu() as a taint source but did not model the ternary sanitisation that eliminates all server-supplied influence on the loop bound.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcele16_to_cpu() line 275
Taint snippetmax_nparts = le16_to_cpu(v->v_nparts) > 8 ? SOLARIS_X86_NUMSLICE : 8;
Tainted varmax_nparts
Loopfor_loop line 276
Sink snippetfor (i = 0; i < max_nparts && state->next < state->limit; i++) {
Possibly guardedno
Dismissed: max_nparts = le16_to_cpu(v->v_nparts) > 8 ? SOLARIS_X86_NUMSLICE : 8 clamps max_nparts to either 16 or 8, both compile-time constants. The v_slice array has SOLARIS_X86_NUMSLICE (16) elements, so i in [0, max_nparts-1] is always within bounds. No counterexample exists: any server-supplied v->v_nparts value, no matter how large, results in max_nparts being at most 16, which is exactly the array size. The taint propagation through the ternary is a false positive from the scanner not modelling the sanitisation.

osf_partition() — block/partitions/osf.c FP confidence=high

The function reads a disk label from a sector buffer and validates d_npartitions against MAX_OSF_PARTITIONS before the loop. The loop iterates at most MAX_OSF_PARTITIONS times, and the d_partitions array in the struct is declared with exactly MAX_OSF_PARTITIONS elements. Additionally, the loop has a secondary guard 'if (slot == state->limit) break' that caps iteration. The check at line 71 (npartitions > MAX_OSF_PARTITIONS → return 0) is the key bound, and since the partition array is sized to MAX_OSF_PARTITIONS, no out-of-bounds access is possible.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcele16_to_cpu() line 70
Taint snippetnpartitions = le16_to_cpu(label->d_npartitions);
Tainted varnpartitions
Loopfor_loop line 75
Sink snippetfor (i = 0 ; i < npartitions; i++, partition++) {
Possibly guardedyes (heuristic)
Dismissed: npartitions is capped at MAX_OSF_PARTITIONS (line 71-74) before the loop. The d_partitions array embedded in the disklabel struct is declared as d_partitions[MAX_OSF_PARTITIONS], so the loop can iterate at most MAX_OSF_PARTITIONS times, staying within the statically-sized array. No counterexample exists: any value of npartitions > MAX_OSF_PARTITIONS causes an early return, and values ≤ MAX_OSF_PARTITIONS keep 'partition' within the array bounds. The finding is a false positive.

sun_partition() — block/partitions/sun.c FP confidence=high

The function reads a disk partition label (external data) and validates nparts <= 8 as a precondition for use_vtoc. The ternary at line 97 only uses the disk-supplied nparts value when use_vtoc is true, which requires nparts <= 8. Otherwise nparts is the constant 8. In both cases nparts <= 8, keeping the loop within the 8-element partitions[] and infos[] arrays.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe16_to_cpu() line 97
Taint snippetnparts = (use_vtoc) ? be16_to_cpu(label->vtoc.nparts) : 8;
Tainted varnparts
Loopfor_loop line 106
Sink snippetfor (i = 0; i < nparts; i++, p++) {
Possibly guardedno
Dismissed: No counterexample exists: use_vtoc is only true when nparts <= 8 (line 94), so the ternary at line 97 never assigns a disk-supplied value > 8 to nparts. When use_vtoc is false, nparts is the constant 8. The loop bound is always <= 8, safely within the 8-element partitions[] and vtoc.infos[] arrays.

sysv68_partition() — block/partitions/sysv68.c FP confidence=high

The scanner misidentifies the loop iteration taint source. 'i' from be32_to_cpu(ios_slcblk) is used as a sector number for read_part_sector(), then immediately reused as a for-loop counter variable initialized to 0 in 'for (i = 0; i < slices; i++, slice++)'. The actual loop bound is 'slices' from ios_slccnt (line 67), which is separately tainted and genuinely unvalidated against the sector buffer size (512 bytes / sizeof(struct slice)). However, the flagged finding #1 specifically attributes the loop iteration taint to 'i' from ios_slcblk, which is incorrect — 'i' is reset to 0 in the for-loop initializer and does not control the iteration count. A real bug may exist regarding 'slices' being unbounded relative to the sector buffer, but that is not what was flagged.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe32_to_cpu() line 68
Taint snippeti = be32_to_cpu(b->dk_ios.ios_slcblk);
Tainted vari
Loopfor_loop line 78
Sink snippetfor (i = 0; i < slices; i++, slice++) {
Possibly guardedno
Dismissed: The scanner incorrectly attributes loop iteration control to 'i' from ios_slcblk. That 'i' is used as a sector number in read_part_sector() then reinitialized to 0 in the for-loop header. The for-loop iteration count is actually controlled by 'slices' (from ios_slccnt, line 67), which is a separate tainted value. No counterexample exists for 'i' from ios_slcblk controlling the loop count, because 'i' is overwritten by the for-loop initializer. The finding as stated is a false positive. Note: there is a separate potential real issue where 'slices' is not validated against the 512-byte sector buffer size before the loop increments 'slice' past the buffer end, but that is not what was flagged here.

opal_discovery0_end() — block/sed-opal.c FP confidence=high

The function properly validates hlen against IO_BUFFER_LENGTH before use. len_out is further capped by min_t(u64, discv_out->size, hlen), bounding both the source read (hlen <= IO_BUFFER_LENGTH - sizeof(*hdr)) and the destination write (len_out <= discv_out->size, user's stated buffer capacity). copy_to_user with a user-supplied size is normal kernel practice; the MMU protects against invalid user pointers. No counterexample can be constructed that passes the line-585 guard yet causes OOB.

Finding #1 — Category B — false positive

CategoryCat B — server value → size/alloc argument
Taint sourcebe32_to_cpu() line 580
Taint snippetu32 hlen = be32_to_cpu(hdr->length);
Tainted varlen_out
Sinkcopy_to_user() line 594 (arg 2, role=size)
Sink snippetif (buf_out && copy_to_user(buf_out, dev->resp, len_out))
Possibly guardedno
Dismissed: hlen is checked at line 585 against IO_BUFFER_LENGTH (source buffer bound). len_out = min_t(u64, discv_out->size, hlen) caps the copy at both user's claimed buffer size and the validated hlen. No OOB read of dev->resp is possible because len_out <= hlen <= IO_BUFFER_LENGTH - sizeof(*hdr) < IO_BUFFER_LENGTH. No counterexample can be constructed.

Finding #2 — Category G2 — false positive

CategoryCat G2 — unvalidated size argument to copy_from/to_user
Taint sourcecopy_to_user() line 594
Taint snippetif (buf_out && copy_to_user(buf_out, dev->resp, len_out))
Tainted varlen_out
Unvalidated sizecopy_to_user() arg 2 line 594 — size len_out
Sink snippetif (buf_out && copy_to_user(buf_out, dev->resp, len_out))
Possibly guardedno
Dismissed: len_out is structurally bounded by min(discv_out->size, hlen). The copy_to_user size argument never exceeds the user's declared buffer capacity (discv_out->size), which is standard practice. The scanner flagged this because discv_out->size is user-supplied, but using the user's own declared size as the copy limit is correct and safe.

response_parse() — block/sed-opal.c FP confidence=medium

The pre-loop check validates slen <= IO_BUFFER_LENGTH - sizeof(*hdr), bounding total iterations. Within the loop, if token_length exceeds remaining total, total goes negative and the while(total > 0) guard exits before the next pos[0] access — preventing OOB in the loop control itself. Potential issues may exist inside the sub-parsers (response_parse_short etc.) reading beyond their bounds, but that is not what this finding flags.

Finding #1 — Category F — false positive

CategoryCat F — server value → loop iteration count
Taint sourcebe32_to_cpu() line 1036
Taint snippetslen = be32_to_cpu(hdr->subpkt.length);
Tainted vartotal
Loopwhile_loop line 1054
Sink snippetwhile (total > 0) {
Possibly guardedno
Dismissed: The check at line 1040-1046 validates slen <= IO_BUFFER_LENGTH - sizeof(*hdr). The loop uses total=slen and while(total > 0). If a sub-parser returns a token_length > total, total goes negative (terminating the loop) before the next pos[0] is read. No counterexample found where the loop itself causes OOB access at pos[0] — the guard is sufficient for iteration control. Sub-parser internal reads could be a separate concern not covered by this finding.