Contents
fs/smb/client/ (67 functions) — 65 real, 167 FP
open_cached_dir() — cached_dir.c FP
build_sec_desc() — cifsacl.c FP
id_mode_to_cifs_acl() — cifsacl.c FP
parse_dacl() — cifsacl.c MIXED
parse_sec_desc() — cifsacl.c FP
replace_sids_and_copy_aces() — cifsacl.c FP
set_chmod_dacl() — cifsacl.c FP
validate_dacl() — cifsacl.c FP
CIFSFindFirst() — cifssmb.c BUG
CIFSFindNext() — cifssmb.c BUG
CIFSGetExtAttr() — cifssmb.c BUG
CIFSGetSrvInodeNumber() — cifssmb.c BUG
CIFSPOSIXCreate() — cifssmb.c VALIDATE
CIFSSMBPosixLock() — cifssmb.c BUG
CIFSSMBQAllEAs() — cifssmb.c MIXED
CIFSSMBQFSAttributeInfo() — cifssmb.c BUG
CIFSSMBQFSDeviceInfo() — cifssmb.c BUG
CIFSSMBQFSInfo() — cifssmb.c BUG
CIFSSMBQFSPosixInfo() — cifssmb.c VALIDATE
CIFSSMBQFSUnixInfo() — cifssmb.c BUG
CIFSSMBRead() — cifssmb.c MIXED
CIFSSMBUnixQuerySymLink() — cifssmb.c MIXED
SMBOldQFSInfo() — cifssmb.c BUG
cifs_create_reparse_inode() — cifssmb.c MIXED
cifs_do_get_acl() — cifssmb.c MIXED
cifs_query_reparse_point() — cifssmb.c BUG
cifs_to_posix_acl() — cifssmb.c FP
parse_dfs_referrals() — misc.c FP
cnvrtDosUnixTm() — netmisc.c FP
mknod_wsl() — reparse.c FP
parse_reparse_native_symlink() — reparse.c FP
parse_reparse_nfs() — reparse.c FP
parse_reparse_wsl_symlink() — reparse.c FP
map_smb_to_linux_error() — smb1maperror.c FP
is_valid_oplock_break() — smb1misc.c BUG
cifs_query_path_info() — smb1ops.c ERROR
coalesce_t2() — smb1transport.c BUG
smb2_parse_symlink_response() — smb2file.c FP
check_wsl_eas() — smb2inode.c FP
parse_posix_sids() — smb2inode.c FP
reparse_buf_ptr() — smb2inode.c BUG
smb2_compound_op() — smb2inode.c FP
__smb2_calc_size() — smb2misc.c VALIDATE
smb2_check_message() — smb2misc.c FP
smb2_tcon_find_pending_open_lease() — smb2misc.c VALIDATE
smb2_tcon_has_lease() — smb2misc.c BUG
crypt_message() — smb2ops.c FP
move_smb2_ea_to_cifs() — smb2ops.c FP
parse_server_interfaces() — smb2ops.c FP
receive_encrypted_standard() — smb2ops.c BUG
smb2_query_eas() — smb2ops.c FP
smb3_enum_snapshots() — smb2ops.c FP
smb3_fiemap() — smb2ops.c FP
smb3_simple_fallocate_range() — smb2ops.c FP
SMB2_QFS_attr() — smb2pdu.c FP
__smb2_plain_req_init() — smb2pdu.c FP
decode_compress_ctx() — smb2pdu.c FP
fill_small_buf() — smb2pdu.c FP
parse_posix_ctxt() — smb2pdu.c FP
posix_info_parse() — smb2pdu.c BUG
query_info() — smb2pdu.c FP
smb2_parse_contexts() — smb2pdu.c FP
smb311_decode_neg_context() — smb2pdu.c FP
smb2_calc_signature() — smb2transport.c FP
smb2_seq_num_into_buf() — smb2transport.c FP
smb3_calc_signature() — smb2transport.c FP
__release_mid() — transport.c FP
Summary
| Function | File | Assessment | Confidence | Real | FP | Unanalyzed |
|---|---|---|---|---|---|---|
| fs/smb/client/ — 67 functions, 65 real, 167 FP | ||||||
| open_cached_dir() | fs/smb/client/cached_dir.c | FP | high | 0 | 1 | 0 |
| build_sec_desc() | fs/smb/client/cifsacl.c | FP | high | 0 | 22 | 0 |
| id_mode_to_cifs_acl() | fs/smb/client/cifsacl.c | FP | high | 0 | 8 | 0 |
| parse_dacl() | fs/smb/client/cifsacl.c | MIXED | medium | 2 | 1 | 0 |
| parse_sec_desc() | fs/smb/client/cifsacl.c | FP | high | 0 | 1 | 0 |
| replace_sids_and_copy_aces() | fs/smb/client/cifsacl.c | FP | medium | 0 | 1 | 0 |
| set_chmod_dacl() | fs/smb/client/cifsacl.c | FP | medium | 0 | 1 | 0 |
| validate_dacl() | fs/smb/client/cifsacl.c | FP | high | 0 | 1 | 0 |
| CIFSFindFirst() | fs/smb/client/cifssmb.c | BUG | medium | 4 | 0 | 0 |
| CIFSFindNext() | fs/smb/client/cifssmb.c | BUG | high | 3 | 0 | 0 |
| CIFSGetExtAttr() | fs/smb/client/cifssmb.c | BUG | high | 2 | 0 | 0 |
| CIFSGetSrvInodeNumber() | fs/smb/client/cifssmb.c | BUG | high | 1 | 0 | 0 |
| CIFSPOSIXCreate() | fs/smb/client/cifssmb.c | VALIDATE | medium | 5 | 0 | 0 |
| CIFSSMBPosixLock() | fs/smb/client/cifssmb.c | BUG | high | 7 | 0 | 0 |
| CIFSSMBQAllEAs() | fs/smb/client/cifssmb.c | MIXED | medium | 3 | 8 | 0 |
| CIFSSMBQFSAttributeInfo() | fs/smb/client/cifssmb.c | BUG | high | 1 | 0 | 0 |
| CIFSSMBQFSDeviceInfo() | fs/smb/client/cifssmb.c | BUG | high | 1 | 0 | 0 |
| CIFSSMBQFSInfo() | fs/smb/client/cifssmb.c | BUG | high | 4 | 0 | 0 |
| CIFSSMBQFSPosixInfo() | fs/smb/client/cifssmb.c | VALIDATE | medium | 9 | 0 | 0 |
| CIFSSMBQFSUnixInfo() | fs/smb/client/cifssmb.c | BUG | high | 1 | 0 | 0 |
| CIFSSMBRead() | fs/smb/client/cifssmb.c | MIXED | high | 1 | 1 | 0 |
| CIFSSMBUnixQuerySymLink() | fs/smb/client/cifssmb.c | MIXED | medium | 2 | 0 | 0 |
| SMBOldQFSInfo() | fs/smb/client/cifssmb.c | BUG | high | 4 | 0 | 0 |
| cifs_create_reparse_inode() | fs/smb/client/cifssmb.c | MIXED | medium | 0 | 2 | 0 |
| cifs_do_get_acl() | fs/smb/client/cifssmb.c | MIXED | medium | 1 | 1 | 0 |
| cifs_query_reparse_point() | fs/smb/client/cifssmb.c | BUG | medium | 3 | 0 | 0 |
| cifs_to_posix_acl() | fs/smb/client/cifssmb.c | FP | medium | 0 | 1 | 0 |
| parse_dfs_referrals() | fs/smb/client/misc.c | FP | high | 0 | 3 | 0 |
| cnvrtDosUnixTm() | fs/smb/client/netmisc.c | FP | high | 0 | 1 | 0 |
| mknod_wsl() | fs/smb/client/reparse.c | FP | high | 0 | 2 | 0 |
| parse_reparse_native_symlink() | fs/smb/client/reparse.c | FP | high | 0 | 10 | 0 |
| parse_reparse_nfs() | fs/smb/client/reparse.c | FP | high | 0 | 1 | 0 |
| parse_reparse_wsl_symlink() | fs/smb/client/reparse.c | FP | high | 0 | 3 | 0 |
| map_smb_to_linux_error() | fs/smb/client/smb1maperror.c | FP | high | 0 | 2 | 0 |
| is_valid_oplock_break() | fs/smb/client/smb1misc.c | BUG | high | 1 | 0 | 0 |
| cifs_query_path_info() | fs/smb/client/smb1ops.c | ERROR | low | 0 | 0 | 27 |
| coalesce_t2() | fs/smb/client/smb1transport.c | BUG | high | 3 | 0 | 0 |
| smb2_parse_symlink_response() | fs/smb/client/smb2file.c | FP | high | 0 | 10 | 0 |
| check_wsl_eas() | fs/smb/client/smb2inode.c | FP | high | 0 | 21 | 0 |
| parse_posix_sids() | fs/smb/client/smb2inode.c | FP | high | 0 | 4 | 0 |
| reparse_buf_ptr() | fs/smb/client/smb2inode.c | BUG | high | 1 | 0 | 0 |
| smb2_compound_op() | fs/smb/client/smb2inode.c | FP | high | 0 | 1 | 0 |
| __smb2_calc_size() | fs/smb/client/smb2misc.c | VALIDATE | medium | 1 | 0 | 0 |
| smb2_check_message() | fs/smb/client/smb2misc.c | FP | high | 0 | 1 | 0 |
| smb2_tcon_find_pending_open_lease() | fs/smb/client/smb2misc.c | VALIDATE | medium | 1 | 0 | 0 |
| smb2_tcon_has_lease() | fs/smb/client/smb2misc.c | BUG | medium | 1 | 0 | 0 |
| crypt_message() | fs/smb/client/smb2ops.c | FP | high | 0 | 1 | 0 |
| move_smb2_ea_to_cifs() | fs/smb/client/smb2ops.c | FP | high | 0 | 2 | 0 |
| parse_server_interfaces() | fs/smb/client/smb2ops.c | FP | high | 0 | 1 | 0 |
| receive_encrypted_standard() | fs/smb/client/smb2ops.c | BUG | medium | 1 | 0 | 0 |
| smb2_query_eas() | fs/smb/client/smb2ops.c | FP | high | 0 | 8 | 0 |
| smb3_enum_snapshots() | fs/smb/client/smb2ops.c | FP | high | 0 | 1 | 0 |
| smb3_fiemap() | fs/smb/client/smb2ops.c | FP | high | 0 | 1 | 0 |
| smb3_simple_fallocate_range() | fs/smb/client/smb2ops.c | FP | high | 0 | 1 | 0 |
| SMB2_QFS_attr() | fs/smb/client/smb2pdu.c | FP | high | 0 | 5 | 0 |
| __smb2_plain_req_init() | fs/smb/client/smb2pdu.c | FP | high | 0 | 1 | 0 |
| decode_compress_ctx() | fs/smb/client/smb2pdu.c | FP | high | 0 | 1 | 0 |
| fill_small_buf() | fs/smb/client/smb2pdu.c | FP | high | 0 | 1 | 0 |
| parse_posix_ctxt() | fs/smb/client/smb2pdu.c | FP | high | 0 | 4 | 0 |
| posix_info_parse() | fs/smb/client/smb2pdu.c | BUG | high | 2 | 0 | 0 |
| query_info() | fs/smb/client/smb2pdu.c | FP | high | 0 | 1 | 0 |
| smb2_parse_contexts() | fs/smb/client/smb2pdu.c | FP | high | 0 | 9 | 0 |
| smb311_decode_neg_context() | fs/smb/client/smb2pdu.c | FP | high | 0 | 9 | 0 |
| smb2_calc_signature() | fs/smb/client/smb2transport.c | FP | high | 0 | 1 | 0 |
| smb2_seq_num_into_buf() | fs/smb/client/smb2transport.c | FP | high | 0 | 1 | 0 |
| smb3_calc_signature() | fs/smb/client/smb2transport.c | FP | high | 0 | 1 | 0 |
| __release_mid() | fs/smb/client/transport.c | FP | high | 0 | 10 | 0 |
Function Details
open_cached_dir() — fs/smb/client/cached_dir.c FP confidence=high
The flagged call to smb2_validate_and_copy_iov() is itself the validation function. It internally calls smb2_validate_iov(offset, buffer_length, iov, minbufsize) before computing begin_of_buf and performing memcpy. The smb2_validate_iov() call checks that offset + buffer_length fits within iov->iov_len, ensuring begin_of_buf points into valid memory. The memcpy inside smb2_validate_and_copy_iov() is only reached after that bounds check passes, so the tainted OutputBufferOffset is validated before use.
Finding #1 — Category A — cross-function via smb2_validate_and_copy_iov() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 371 |
| Taint snippet | if (!smb2_validate_and_copy_iov( |
| Tainted var | le16_to_cpu(qi_rsp->OutputBufferOffset) |
| Call site | line 371 — passes le16_to_cpu(qi_rsp->OutputBufferOffset) to smb2_validate_and_copy_iov() |
| Call snippet | if (!smb2_validate_and_copy_iov( |
| Sink (in callee) | memcpy() line 3868 (arg 1, role=pointer) |
| Sink snippet | memcpy(data, begin_of_buf, minbufsize); |
| Possibly guarded | no |
Dismissed: smb2_validate_and_copy_iov() is a combined validation-and-copy helper: it calls smb2_validate_iov(offset, buffer_length, iov, minbufsize) which verifies that offset + buffer_length does not exceed iov->iov_len before the memcpy is reached. The tainted OutputBufferOffset is the 'offset' parameter; if it is out of range, smb2_validate_iov returns an error and memcpy is never executed. No counterexample can be constructed: any offset that would cause OOB would also cause smb2_validate_iov to fail. This is a false positive — the scanner flagged the internal memcpy inside the validation function itself.
build_sec_desc() — fs/smb/client/cifsacl.c FP confidence=high
build_sec_desc() has solid validation discipline. When dacloffset is non-zero, dacl_offset_valid() verifies the offset is within buffer bounds before dacl_ptr is created, and validate_dacl() performs a complete per-element traversal of all ACEs with bounds checks before any downstream use. All flagged accesses (dacl_ptr->revision, dacl_ptr->num_aces, set_chmod_dacl loop, replace_sids_and_copy_aces loop) occur only after these validators have returned 0. Findings #5-#10 are the validator's own internal logic. Finding #4 misidentifies a u16-returning function assigned to u16 as a 32->16 truncation. | build_sec_desc() has a thorough validation discipline: dacl_offset_valid() confirms the DACL fits within the buffer, and validate_dacl() walks every ACE with per-element bounds checks before any DACL traversal occurs. replace_sids_and_copy_aces() uses structurally identical arithmetic (same base pointer, same per-element size field, same iteration count) as validate_dacl(), so its traversal is already proven safe by the prior validation gate. The call at line 1529 is additionally guarded by `if (dacloffset)`, ensuring dacl_ptr is non-NULL only when fully validated.
Finding #1 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Pointer deref | dacl_ptr->revision line 1447 |
| Sink snippet | dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); |
| Possibly guarded | no |
Dismissed: dacl_ptr->revision at line 1447 is only reached when dacloffset != 0, after dacl_offset_valid() and validate_dacl() both pass. validate_dacl() checks end_of_acl >= (char*)pdacl + sizeof(struct smb_acl), which covers the revision field. No counterexample can be constructed: any dacloffset that passes dacl_offset_valid() places pdacl at least sizeof(struct smb_acl) bytes before end_of_acl.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Pointer deref | dacl_ptr->revision line 1467 |
| Sink snippet | dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); |
| Possibly guarded | no |
Dismissed: Same as finding #1 — dacl_ptr->revision at line 1467 in the else branch. Same validation chain (dacl_offset_valid + validate_dacl) protects this access. False positive.
Finding #3 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Pointer deref | dacl_ptr->num_aces line 1468 |
| Sink snippet | ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; |
| Possibly guarded | no |
Dismissed: dacl_ptr->num_aces at line 1468 is guarded by 'dacl_ptr ? ... : 0', and dacl_ptr is non-null only after dacl_offset_valid() + validate_dacl() pass. num_aces is within sizeof(struct smb_acl) from pdacl base, which validate_dacl() confirms fits in the buffer. False positive.
Finding #4 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le32_to_cpu() line 1529 |
| Taint snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Tainted var | size |
| Truncation | line 1529: 32 → 16-bit u16 |
| Sink snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Possibly guarded | no |
Dismissed: replace_sids_and_copy_aces() is declared as returning __u16 and the result is assigned to u16 size. There is no 32-to-16 bit truncation here. The scanner incorrectly identified this as a truncation. False positive.
Finding #5 — Category F — cross-function via validate_dacl() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Loop | for_loop line 875 |
| Sink snippet | for (i = 0; i < num_aces; ++i) { |
| Possibly guarded | yes (heuristic) |
Dismissed: validate_dacl() IS the validation function. The for loop at line 875 iterates over ACEs and is the bounds-checking logic itself. Accesses inside the validator are the validation, not vulnerable sinks. False positive.
Finding #6 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Pointer deref | dacl_ptr-> line 885 |
| Sink snippet | pace->sid.num_subauth == 0 || |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->sid.num_subauth access at line 885 is inside validate_dacl(), which first checks end_of_dacl - acl_base >= ace_hdr_size before this dereference. This is part of the validation logic. False positive.
Finding #7 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Pointer deref | dacl_ptr-> line 886 |
| Sink snippet | pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as #6 — pace->sid.num_subauth at line 886 is within the same guarded block inside the validator. False positive.
Finding #8 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Pointer deref | dacl_ptr-> line 891 |
| Sink snippet | ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->sid.num_subauth at line 891 is used after the bounds check at line 884 confirms ace_hdr_size fits. This is validation logic internal to validate_dacl(). False positive.
Finding #9 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Pointer deref | dacl_ptr-> line 893 |
| Sink snippet | le16_to_cpu(pace->size) < ace_size) { |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->size at line 893 is read inside validate_dacl() after confirming end_of_dacl - acl_base >= ace_size. Validation logic. False positive.
Finding #10 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1425 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, end_of_acl); |
| Pointer deref | dacl_ptr-> line 898 |
| Sink snippet | ace_size = le16_to_cpu(pace->size); |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->size at line 898 is read inside validate_dacl() after all prior checks in the loop pass. This is validation logic. False positive.
Finding #11 — Category F — cross-function via set_chmod_dacl() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Loop | for_loop line 1266 |
| Sink snippet | for (i = 0; i < src_num_aces; ++i) { |
| Possibly guarded | no |
Dismissed: set_chmod_dacl() is called at line 1452 only when pnmode && *pnmode != NO_CHANGE_64. dacl_ptr may be NULL here (no dacloffset check before call), but set_chmod_dacl() handles NULL pdacl at line 1253 ('if (!pdacl || posix)'). When dacl_ptr is non-null, validate_dacl() has already confirmed all ACEs fit within bounds. The loop iterates src_num_aces times with the same arithmetic. No counterexample: validate_dacl ensures the entire DACL including all ACEs fits within end_of_acl. False positive.
Finding #12 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1269 |
| Sink snippet | if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { |
| Possibly guarded | no |
Dismissed: pntace->flags access in set_chmod_dacl at line 1269 — pntace is computed as acl_base + size where size starts at sizeof(struct smb_acl) and advances by validated ace sizes. validate_dacl() confirmed all ACE accesses are in bounds. False positive.
Finding #13 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1280 |
| Sink snippet | if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || |
| Possibly guarded | no |
Dismissed: pntace->sid access in set_chmod_dacl at line 1280. Same argument as #12 — validate_dacl() walked all ACEs including sid fields. False positive.
Finding #14 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1281 |
| Sink snippet | (compare_sids(&pntace->sid, pownersid) == 0) || |
| Possibly guarded | yes (heuristic) |
Dismissed: pntace->sid at line 1281 in set_chmod_dacl. Protected by validate_dacl() pre-validation. False positive.
Finding #15 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1282 |
| Sink snippet | (compare_sids(&pntace->sid, pgrpsid) == 0) || |
| Possibly guarded | yes (heuristic) |
Dismissed: pntace->sid at line 1282 in set_chmod_dacl. Protected by validate_dacl() pre-validation. False positive.
Finding #16 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1283 |
| Sink snippet | (compare_sids(&pntace->sid, &sid_everyone) == 0) || |
| Possibly guarded | yes (heuristic) |
Dismissed: pntace->sid at line 1283 in set_chmod_dacl. Protected by validate_dacl() pre-validation. False positive.
Finding #17 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1284 |
| Sink snippet | (compare_sids(&pntace->sid, &sid_authusers) == 0))) { |
| Possibly guarded | yes (heuristic) |
Dismissed: pntace->sid at line 1284 in set_chmod_dacl. Protected by validate_dacl() pre-validation. False positive.
Finding #18 — Category E — cross-function via set_chmod_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1452 — passes dacl_ptr to set_chmod_dacl() |
| Call snippet | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, |
| Pointer deref | dacl_ptr-> line 1295 |
| Sink snippet | size += le16_to_cpu(pntace->size); |
| Possibly guarded | yes (heuristic) |
Dismissed: pntace->size at line 1295 in set_chmod_dacl. validate_dacl() verified all ace->size values are valid and fit within the DACL. The loop traversal is structurally identical to validate_dacl()'s traversal. False positive.
Finding #19 — Category F — cross-function via replace_sids_and_copy_aces() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1529 — passes dacl_ptr to replace_sids_and_copy_aces() |
| Call snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Loop | for_loop line 1212 |
| Sink snippet | for (i = 0; i < src_num_aces; ++i) { |
| Possibly guarded | no |
Dismissed: replace_sids_and_copy_aces() at line 1529 is called only when dacloffset is non-zero (line 1527 guard), meaning dacl_offset_valid() and validate_dacl() already passed at lines 1419-1427. The loop iterates pdacl->num_aces times with identical arithmetic to validate_dacl(). Container-level validation covers this traversal. No counterexample possible. False positive.
Finding #20 — Category E — cross-function via replace_sids_and_copy_aces() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1529 — passes dacl_ptr to replace_sids_and_copy_aces() |
| Call snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Pointer deref | dacl_ptr-> line 1216 |
| Sink snippet | if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) { |
| Possibly guarded | no |
Dismissed: pntace->sid access in replace_sids_and_copy_aces() at line 1216. Same protection as finding #19 — validate_dacl() confirmed all ACE sid accesses are in bounds before this function is called. False positive.
Finding #21 — Category E — cross-function via replace_sids_and_copy_aces() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1529 — passes dacl_ptr to replace_sids_and_copy_aces() |
| Call snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Pointer deref | dacl_ptr-> line 1219 |
| Sink snippet | } else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: dacl_ptr is derived from server-supplied dacloffset, but is fully validated before use: dacl_offset_valid() checks the offset is within the buffer, and validate_dacl() walks all ACEs verifying each ACE's size and SID fit within [dacl_ptr, end_of_acl]. replace_sids_and_copy_aces() is called inside `if (dacloffset)` so dacl_ptr is non-NULL. The traversal in replace_sids_and_copy_aces() at line 1219 accesses pntace->sid, which validate_dacl() already proved is within bounds. No counterexample can be constructed: any ACE whose SID or size would go out of range would have caused validate_dacl() to return an error before reaching line 1529.
Finding #22 — Category E — cross-function via replace_sids_and_copy_aces() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1417 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1529 — passes dacl_ptr to replace_sids_and_copy_aces() |
| Call snippet | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, |
| Pointer deref | dacl_ptr-> line 1226 |
| Sink snippet | size += le16_to_cpu(pntace->size); |
| Possibly guarded | no |
Dismissed: The sink at line 1226 (`size += le16_to_cpu(pntace->size)`) reads pntace->size from the validated DACL. validate_dacl() has already walked all ACEs using the same arithmetic and verified each ACE's size field fits within the buffer. The loop in replace_sids_and_copy_aces() iterates src_num_aces times (= le16_to_cpu(pdacl->num_aces)), the same count used by validate_dacl(). No counterexample exists: any pntace->size that would overflow or go OOB would have been caught by validate_dacl(). This is a false positive.
id_mode_to_cifs_acl() — fs/smb/client/cifsacl.c FP confidence=high
All 8 findings are false positives. The code follows a clear validation discipline: (1) dacl_offset_valid() is called before dacl_ptr is constructed, verifying that dacloffset is within bounds and that there is room for at least sizeof(struct smb_acl) at that offset; (2) validate_dacl() is then called with dacl_ptr before any fields of dacl_ptr are accessed in the caller; (3) findings #3-#8 flag accesses *inside* validate_dacl() itself — those accesses ARE the validation logic, not vulnerable sinks; (4) findings #1 and #2 (accessing dacl_ptr->num_aces and dacl_ptr->size in the caller) occur only AFTER validate_dacl() returns 0, meaning the dacl has already been fully traversed and validated. validate_dacl() checks: (a) end_of_acl >= pdacl + sizeof(smb_acl), establishing the struct header is readable; (b) dacl_size >= sizeof(smb_acl) and end_of_acl >= pdacl + dacl_size, so the full DACL fits; (c) num_aces is bounded by (dacl_size - sizeof(smb_acl)) / min_ace_size; (d) every ACE is bounds-checked in the loop. After validate_dacl() returns 0, accessing dacl_ptr->num_aces and dacl_ptr->size is safe.
Finding #1 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Pointer deref | dacl_ptr->num_aces line 1820 |
| Sink snippet | le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); |
| Possibly guarded | no |
Dismissed: dacl_ptr->num_aces is accessed at line 1820 only after dacl_offset_valid() (line 1805) confirms the offset is valid and validate_dacl() (line 1812) returns 0, which verifies end_of_acl >= pdacl + sizeof(smb_acl). Could not construct a counterexample: dacl_offset_valid ensures dacloffset + sizeof(smb_acl) <= secdesclen, and validate_dacl checks the header fits before reading any field. No OOB possible.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Pointer deref | dacl_ptr->size line 1822 |
| Sink snippet | nsecdesclen += le16_to_cpu(dacl_ptr->size); |
| Possibly guarded | no |
Dismissed: dacl_ptr->size is accessed at line 1822 only after validate_dacl() returns 0. validate_dacl() verifies dacl_size >= sizeof(smb_acl) and end_of_acl >= pdacl + dacl_size, so the size field is safe to read. No counterexample exists.
Finding #3 — Category F — cross-function via validate_dacl() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Loop | for_loop line 875 |
| Sink snippet | for (i = 0; i < num_aces; ++i) { |
| Possibly guarded | yes (heuristic) |
Dismissed: The sink is the for loop INSIDE validate_dacl() itself. validate_dacl() is the validation function — the loop bound (num_aces) is validated at line 866 against (dacl_size - sizeof(smb_acl)) / min_ace_size before the loop executes. Accesses inside the validator are the validation logic, not vulnerable sinks.
Finding #4 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Pointer deref | dacl_ptr-> line 885 |
| Sink snippet | pace->sid.num_subauth == 0 || |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->sid.num_subauth at line 885 is accessed inside validate_dacl() after the bounds check at line 884 (end_of_dacl - acl_base >= ace_hdr_size), which covers offsetof(smb_ace, sid) + offsetof(smb_sid, sub_auth), ensuring num_subauth is readable. This is part of the validation logic.
Finding #5 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Pointer deref | dacl_ptr-> line 886 |
| Sink snippet | pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as finding #4 — pace->sid.num_subauth at line 886 is covered by the ace_hdr_size check at line 884. Part of validation logic.
Finding #6 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Pointer deref | dacl_ptr-> line 891 |
| Sink snippet | ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->sid.num_subauth at line 891 is accessed after the check at line 884-886 validates it is non-zero and <= SID_MAX_SUB_AUTHORITIES. The subsequent multiplication is safe. Part of validation logic.
Finding #7 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Pointer deref | dacl_ptr-> line 893 |
| Sink snippet | le16_to_cpu(pace->size) < ace_size) { |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->size at line 893 is accessed after end_of_dacl - acl_base >= ace_size check at line 892, which ensures at least ace_size bytes are available, covering the ace header including the size field. Part of validation logic.
Finding #8 — Category E — cross-function via validate_dacl() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1803 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1812 — passes dacl_ptr to validate_dacl() |
| Call snippet | rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); |
| Pointer deref | dacl_ptr-> line 898 |
| Sink snippet | ace_size = le16_to_cpu(pace->size); |
| Possibly guarded | yes (heuristic) |
Dismissed: pace->size at line 898 is accessed after the compound check at lines 892-896 confirms bounds. Part of validation logic inside validate_dacl().
parse_dacl() — fs/smb/client/cifsacl.c MIXED confidence=medium
parse_dacl() calls validate_dacl() before operating on the DACL, but validate_dacl() source is not available. The main loop (finding #1) lacks visible per-iteration bounds checks against end_of_acl, making it potentially vulnerable if validate_dacl() doesn't perform a full ACE traversal. Finding #2 concerns dump_ace() which only runs under CONFIG_CIFS_DEBUG2 and involves an OOB read via num_subauth. Finding #3 is a clear false positive due to min() bounding the iteration.
Finding #1 — Category F — BUG oob_read
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 942 |
| Taint snippet | num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | num_aces |
| Loop | for_loop line 950 |
| Sink snippet | for (i = 0; i < num_aces; ++i) { |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds in parse_dacl when server supplies inflated num_aces or malformed per-ACE size fields, causing the loop to walk past end_of_acl
Fix: Add a per-iteration bounds check inside the loop: before dereferencing ppace[i], verify that (acl_base + acl_size + sizeof(struct smb_ace)) <= end_of_acl, and that le16_to_cpu(ppace[i]->size) >= sizeof(struct smb_ace) and that acl_base + acl_size + le16_to_cpu(ppace[i]->size) <= end_of_acl; break out of the loop if any check fails.
CVE pattern: SMB ACL parsing OOB read via unchecked server-supplied iteration count and per-element size field
Finding #2 — Category F — cross-function via dump_ace() (read-only callee) — BUG oob_read
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 942 |
| Taint snippet | num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | ppace |
| Call site | line 954 — passes ppace to dump_ace() |
| Call snippet | dump_ace(ppace[i], |
| Loop | for_loop line 824 |
| Sink snippet | for (i = 0; i < num_subauth; ++i) { |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: Under CONFIG_CIFS_DEBUG2: KASAN: slab-out-of-bounds in dump_ace when pace->sid.num_subauth exceeds SID_MAX_SUB_AUTHORITIES (15), causing out-of-bounds read of sub_auth[] array
Fix: In dump_ace(), clamp num_subauth: num_subauth = min_t(u8, pace->sid.num_subauth, SID_MAX_SUB_AUTHORITIES) before the loop, or add an early return if num_subauth > SID_MAX_SUB_AUTHORITIES.
CVE pattern: SID num_subauth OOB read in debug path
Finding #3 — Category F — cross-function via compare_sids() (read-only callee) — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 942 |
| Taint snippet | num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | ppace |
| Call site | line 959 — passes ppace to compare_sids() |
| Call snippet | (compare_sids(&(ppace[i]->sid), |
| Loop | for_loop line 194 |
| Sink snippet | for (i = 0; i < num_subauth; ++i) { |
| Possibly guarded | yes (heuristic) |
Dismissed: compare_sids() computes num_subauth = min(num_sat, num_saw) at line 192, bounding the loop to the minimum of both SIDs' subauth counts. No counterexample exists: any value of num_subauth from either SID is bounded by min(), so the loop cannot exceed either SID's sub_auth[] array size (though num_subauth itself is not clamped to SID_MAX_SUB_AUTHORITIES, the min ensures we never exceed what either pointer actually stores). This is a false positive.
parse_sec_desc() — fs/smb/client/cifsacl.c FP confidence=high
parse_sec_desc() has a solid validation chain. The dacloffset is validated by dacl_offset_valid() before dacl_ptr is computed. Inside parse_dacl(), validate_dacl() is called (line 927) before the loop at line 950 that uses num_aces. The loop bound (num_aces) comes from pdacl->num_aces, not directly from dacloffset. validate_dacl() is a dedicated validator that performs per-ACE bounds checks during traversal, establishing that num_aces and the ACE sizes are consistent with the buffer bounds defined by end_of_acl. The taint chain through dacloffset -> dacl_ptr -> num_aces is fully protected by two layers of validation.
Finding #1 — Category F — cross-function via parse_dacl() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1348 |
| Taint snippet | dacloffset = le32_to_cpu(pntsd->dacloffset); |
| Tainted var | dacl_ptr |
| Call site | line 1385 — passes dacl_ptr to parse_dacl() |
| Call snippet | parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, |
| Loop | for_loop line 950 |
| Sink snippet | for (i = 0; i < num_aces; ++i) { |
| Possibly guarded | yes (heuristic) |
Dismissed: Two guards protect this path. First, dacl_offset_valid() at line 1379 validates that dacloffset falls within [sizeof(smb_ntsd), acl_len - sizeof(smb_acl)], making the dacl_ptr dereference safe. Second, parse_dacl() calls validate_dacl(pdacl, end_of_acl) at line 927 before the flagged loop at line 950. validate_dacl() is a dedicated DACL validator that walks the ACE array with per-element bounds checks against end_of_acl, establishing that num_aces and all ACE sizes are consistent with the available buffer. No counterexample can be constructed: any dacloffset that passes dacl_offset_valid() yields a dacl_ptr pointing to a valid smb_acl header within the buffer, and any num_aces that passes validate_dacl() is proven safe for the subsequent loop. The finding is a false positive.
replace_sids_and_copy_aces() — fs/smb/client/cifsacl.c FP confidence=medium
The function replace_sids_and_copy_aces() uses server-supplied num_aces to control loop iteration, but its sole call site in build_sec_desc() calls validate_dacl(dacl_ptr, end_of_acl) before reaching the call. validate_dacl() is expected to perform a full traversal of the DACL with per-element bounds checks using the same base pointer, num_aces count, and per-element size arithmetic, establishing a postcondition that makes the loop in replace_sids_and_copy_aces() safe. The 'prior full-traversal' protection pattern applies here.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 1206 |
| Taint snippet | src_num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | src_num_aces |
| Loop | for_loop line 1212 |
| Sink snippet | for (i = 0; i < src_num_aces; ++i) { |
| Possibly guarded | no |
Dismissed: validate_dacl(dacl_ptr, end_of_acl) is called at line 1425 in build_sec_desc() before replace_sids_and_copy_aces() is called at line 1529. If validate_dacl performs a full traversal of ACEs using structurally equivalent arithmetic (same pdacl base, same num_aces, same per-element pntace->size), it establishes a precondition that prevents OOB in the subsequent loop. No counterexample can be constructed because any num_aces value that would cause OOB would have been rejected by validate_dacl. Confidence is medium rather than high because validate_dacl source is not shown in the prompt, making this inference based on naming conventions and call patterns.
set_chmod_dacl() — fs/smb/client/cifsacl.c FP confidence=medium
The caller build_sec_desc() calls validate_dacl(dacl_ptr, end_of_acl) before set_chmod_dacl(). This validator appears to perform a full traversal of the DACL with per-ACE bounds checking using the same base pointer, num_aces count, and per-element size field advancement. The loop in set_chmod_dacl() uses structurally identical arithmetic over the same buffer, so the prior traversal in validate_dacl establishes safety for this loop. Without the validate_dacl source we cannot be 100% certain, but the naming convention, return-value pattern, and end_of_acl parameter strongly indicate comprehensive validation.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 1263 |
| Taint snippet | src_num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | src_num_aces |
| Loop | for_loop line 1266 |
| Sink snippet | for (i = 0; i < src_num_aces; ++i) { |
| Possibly guarded | no |
Dismissed: The caller invokes validate_dacl(dacl_ptr, end_of_acl) before calling set_chmod_dacl(). validate_dacl is expected to fully traverse the DACL with per-ACE bounds checks against end_of_acl, establishing that all num_aces ACEs are within bounds. The loop in set_chmod_dacl() uses the same arithmetic (acl_base, size += le16_to_cpu(pntace->size), i < src_num_aces), so it is protected by the prior traversal. Could not construct a counterexample that passes validate_dacl yet causes OOB in this loop, assuming validate_dacl is a proper full-traversal validator. Confidence is medium rather than high due to not having validate_dacl source included.
validate_dacl() — fs/smb/client/cifsacl.c FP confidence=high
validate_dacl() has strong validation discipline: a pre-loop density check bounds num_aces relative to the buffer size, and every iteration of the loop checks remaining buffer space before dereferencing pace. The per-iteration guards (end_of_dacl - acl_base < ace_size/ace_hdr_size) provide form (b) protection — the loop terminates with -EINVAL before any OOB access, regardless of how large num_aces is.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 859 |
| Taint snippet | num_aces = le16_to_cpu(pdacl->num_aces); |
| Tainted var | num_aces |
| Loop | for_loop line 875 |
| Sink snippet | for (i = 0; i < num_aces; ++i) { |
| Possibly guarded | yes (heuristic) |
Dismissed: Two layers of protection: (1) pre-loop density check at line 866 ensures num_aces * min_ace_size <= available buffer space, so the loop cannot iterate more times than there are minimum-size ACEs in the buffer; (2) per-iteration bounds checks before every pointer dereference (lines 876, 884, 892, 899) return -EINVAL if the remaining buffer is too small. No counterexample can be constructed: any num_aces value that passes the density check will be exhausted by per-iteration guards before any OOB access occurs.
CIFSFindFirst() — fs/smb/client/cifssmb.c BUG confidence=medium
validate_t2() is called before accessing parms, but its validation may not guarantee that ParameterOffset + sizeof(T2_FFIRST_RSP_PARMS) fits within the buffer. A malicious server could provide a ParameterOffset that causes parms to point near the end of the buffer, allowing field accesses to read beyond it. The lnoff guard on line 4516 only checks lnoff against CIFSMaxBufSize but not against actual packet boundaries, and doesn't protect the earlier parms field accesses.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4508 |
| Taint snippet | parms = (T2_FFIRST_RSP_PARMS *)((char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->EndofSearch line 4510 |
| Sink snippet | psrch_inf->endOfSearch = !!parms->EndofSearch; |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindFirst when accessing parms->EndofSearch with a crafted ParameterOffset near end of buffer
Fix: After computing parms, verify that (char*)parms + sizeof(T2_FFIRST_RSP_PARMS) <= (char*)pSMBr + bytes_returned before accessing any parms fields.
CVE pattern: CVE-2022-NNNN style DataOffset/ParameterOffset OOB in SMBv1 transaction2 response parsing
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4508 |
| Taint snippet | parms = (T2_FFIRST_RSP_PARMS *)((char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->SearchCount line 4512 |
| Sink snippet | psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindFirst when accessing parms->SearchCount with crafted ParameterOffset
Fix: Validate (char*)parms + sizeof(T2_FFIRST_RSP_PARMS) <= (char*)pSMBr + bytes_returned before accessing any parms fields.
CVE pattern: CVE-2022-NNNN style ParameterOffset OOB in SMBv1 TRANS2 response
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4508 |
| Taint snippet | parms = (T2_FFIRST_RSP_PARMS *)((char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->LastNameOffset line 4515 |
| Sink snippet | lnoff = le16_to_cpu(parms->LastNameOffset); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindFirst when accessing parms->LastNameOffset with crafted ParameterOffset
Fix: Validate (char*)parms + sizeof(T2_FFIRST_RSP_PARMS) <= (char*)pSMBr + bytes_returned before accessing parms fields.
CVE pattern: CVE-2022-NNNN style ParameterOffset OOB in SMBv1 TRANS2 response
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4508 |
| Taint snippet | parms = (T2_FFIRST_RSP_PARMS *)((char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->SearchHandle line 4522 |
| Sink snippet | *pnetfid = parms->SearchHandle; |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindFirst when accessing parms->SearchHandle with crafted ParameterOffset
Fix: Validate (char*)parms + sizeof(T2_FFIRST_RSP_PARMS) <= (char*)pSMBr + bytes_returned before accessing any parms fields.
CVE pattern: CVE-2022-NNNN style ParameterOffset OOB in SMBv1 TRANS2 response
CIFSFindNext() — fs/smb/client/cifssmb.c BUG confidence=high
validate_t2() is called at line 4610 before the parms pointer is constructed. The critical question is what validate_t2() checks. It validates the T2 response header structure but does NOT verify that ParameterOffset + sizeof(T2_FNEXT_RSP_PARMS) lies within the received packet bounds. The ParameterOffset comes directly from the server (le16_to_cpu(pSMBr->t2.ParameterOffset) at line 4618) and can point anywhere in the received buffer or beyond. There is no check that the derived 'parms' pointer (and the size of T2_FNEXT_RSP_PARMS) fits within the actual received packet before accessing parms->EndofSearch, parms->SearchCount, and parms->LastNameOffset. The lnoff check at line 4635 only validates lnoff against CIFSMaxBufSize (not the actual bytes_returned), and occurs after parms fields have already been dereferenced. All three findings share the same root cause: unvalidated ParameterOffset from the server.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4617 |
| Taint snippet | response_data = (char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->EndofSearch line 4631 |
| Sink snippet | psrch_inf->endOfSearch = !!parms->EndofSearch; |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindNext when server sends a ParameterOffset placing parms beyond the received packet buffer, causing out-of-bounds read of parms->EndofSearch
Fix: After validate_t2() succeeds, verify that le16_to_cpu(pSMBr->t2.ParameterOffset) + sizeof(T2_FNEXT_RSP_PARMS) <= bytes_returned (or the actual received buffer size) before casting response_data to T2_FNEXT_RSP_PARMS*. Return -EIO if the check fails.
CVE pattern: SMB response DataOffset/ParameterOffset OOB read (similar to CVE-2011-1090 style T2 parameter offset validation issues)
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4617 |
| Taint snippet | response_data = (char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->SearchCount line 4632 |
| Sink snippet | psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindNext when server sends a ParameterOffset placing parms beyond the received packet buffer, causing out-of-bounds read of parms->SearchCount
Fix: Same as finding #1: validate ParameterOffset + sizeof(T2_FNEXT_RSP_PARMS) <= bytes_returned before using the parms pointer.
CVE pattern: SMB response DataOffset/ParameterOffset OOB read
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4617 |
| Taint snippet | response_data = (char *)&pSMBr->hdr.Protocol + |
| Tainted var | parms |
| Pointer deref | parms->LastNameOffset line 4634 |
| Sink snippet | lnoff = le16_to_cpu(parms->LastNameOffset); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSFindNext; even if parms pointer access doesn't OOB, the lnoff check at line 4635 compares against CIFSMaxBufSize rather than actual bytes_returned, so a value just below CIFSMaxBufSize but beyond actual received data causes OOB read when computing psrch_inf->last_entry
Fix: Same as finding #1 for the parms pointer access. Additionally, the lnoff check should compare against the actual DataCount/received data length rather than CIFSMaxBufSize.
CVE pattern: SMB response DataOffset/ParameterOffset OOB read
CIFSGetExtAttr() — fs/smb/client/cifssmb.c BUG confidence=high
pSMBr is a server response buffer. data_offset is read directly from the server-supplied DataOffset field. The only check performed before using it is that DataCount == 16, which validates the size of the data but does NOT validate that data_offset places the 16-byte struct within the bounds of the received packet. A malicious server could supply a DataOffset that, when added to &pSMBr->hdr.Protocol, points outside the received buffer. validate_t2() is called, but it does not validate DataOffset against packet_end — it only validates TotalDataCount and BCC. There is no check that data_offset + sizeof(*pfinfo) <= bytes_returned (or packet end). This is a genuine OOB read vulnerability.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 3695 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | pfinfo |
| Pointer deref | pfinfo->mode line 3707 |
| Sink snippet | *pExtAttrBits = le64_to_cpu(pfinfo->mode); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSGetExtAttr reading pfinfo->mode; or kernel panic / info disclosure if a malicious server sets DataOffset to push pfinfo beyond the received buffer
Fix: After validating count==16, also verify that data_offset is sane: check that (char *)&pSMBr->hdr.Protocol + data_offset + sizeof(*pfinfo) <= (char *)pSMBr + bytes_returned (i.e., does not exceed the end of the received packet). Return -EIO if the check fails.
CVE pattern: DataOffset OOB read in SMB/CIFS response parsing, similar to CVE-2011-1013 style CIFS DataOffset validation bugs
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 3695 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | pfinfo |
| Pointer deref | pfinfo->mask line 3708 |
| Sink snippet | *pMask = le64_to_cpu(pfinfo->mask); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSGetExtAttr reading pfinfo->mask; same root cause as finding #1, just the second field of the same out-of-bounds struct pointer
Fix: Same fix as finding #1 — validate data_offset + sizeof(struct file_chattr_info) <= packet_end before computing pfinfo. Both ->mode and ->mask accesses are covered by the single bounds check.
CVE pattern: DataOffset OOB read in SMB/CIFS response parsing, same as finding #1
CIFSGetSrvInodeNumber() — fs/smb/client/cifssmb.c BUG confidence=high
The function validates that DataCount >= 8 and that BCC >= 2, and calls validate_t2() which checks basic T2 response structure, but does NOT validate that data_offset + sizeof(*pfinfo) falls within the actual received packet bounds. A malicious server could supply a DataOffset value that, when added to &pSMBr->hdr.Protocol, points outside the packet buffer, causing an out-of-bounds read.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4762 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | pfinfo |
| Pointer deref | pfinfo->UniqueId line 4774 |
| Sink snippet | *inode_number = le64_to_cpu(pfinfo->UniqueId); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSGetSrvInodeNumber or kernel crash when dereferencing pfinfo->UniqueId with a crafted DataOffset value
Fix: After computing pfinfo, verify that the derived pointer plus sizeof(*pfinfo) does not exceed the end of the received response buffer. Specifically: char *buf_end = (char *)pSMBr + bytes_returned; if ((char *)pfinfo + sizeof(*pfinfo) > buf_end || (char *)pfinfo < (char *)&pSMBr->hdr.Protocol) { rc = -EIO; goto GetInodeNumOut; }
CVE pattern: CVE-2022-NNNN style DataOffset OOB — server-controlled T2 DataOffset used without upper-bound validation against packet length, similar to historical CIFS SMB1 DataOffset OOB vulnerabilities
CIFSPOSIXCreate() — fs/smb/client/cifssmb.c VALIDATE confidence=medium
The function validates the T2 response via validate_t2() and checks BCC >= sizeof(OPEN_PSX_RSP) before using psx_rsp. However, the DataOffset used to compute psx_rsp is server-supplied and the BCC check alone does not bound the DataOffset value. If validate_t2() verifies DataOffset+DataCount fits within the response buffer (which is typical for CIFS T2 validators), then all findings are false positives. Without the validate_t2() source, the sufficiency cannot be confirmed with certainty. The memcpy source (finding #5) has an additional guard checking BCC >= sizeof(OPEN_PSX_RSP)+sizeof(FILE_UNIX_BASIC_INFO), but this still doesn't directly constrain DataOffset.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 1132 |
| Taint snippet | psx_rsp = (OPEN_PSX_RSP *) |
| Tainted var | psx_rsp |
| Pointer deref | psx_rsp->OplockFlags line 1135 |
| Sink snippet | *pOplock = le16_to_cpu(psx_rsp->OplockFlags); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSPOSIXCreate when server supplies a crafted DataOffset placing psx_rsp outside the response buffer
Fix: After computing psx_rsp, verify that (char*)psx_rsp + sizeof(OPEN_PSX_RSP) <= (char*)pSMBr + total_response_size. The DataOffset must be checked to ensure psx_rsp falls within the actual response buffer, not just that BCC is large enough.
CVE pattern: DataOffset out-of-bounds in SMB T2 response parsing
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 1132 |
| Taint snippet | psx_rsp = (OPEN_PSX_RSP *) |
| Tainted var | psx_rsp |
| Pointer deref | psx_rsp->Fid line 1137 |
| Sink snippet | *netfid = psx_rsp->Fid; /* cifs fid stays in le */ |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading psx_rsp->Fid in CIFSPOSIXCreate
Fix: Same as finding #1: validate DataOffset before computing psx_rsp pointer.
CVE pattern: DataOffset out-of-bounds in SMB T2 response parsing
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 1132 |
| Taint snippet | psx_rsp = (OPEN_PSX_RSP *) |
| Tainted var | psx_rsp |
| Pointer deref | psx_rsp->CreateAction line 1140 |
| Sink snippet | if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading psx_rsp->CreateAction in CIFSPOSIXCreate
Fix: Same as finding #1: validate DataOffset before computing psx_rsp pointer.
CVE pattern: DataOffset out-of-bounds in SMB T2 response parsing
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 1132 |
| Taint snippet | psx_rsp = (OPEN_PSX_RSP *) |
| Tainted var | psx_rsp |
| Pointer deref | psx_rsp->ReturnedLevel line 1143 |
| Sink snippet | if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading psx_rsp->ReturnedLevel in CIFSPOSIXCreate
Fix: Same as finding #1: validate DataOffset before computing psx_rsp pointer.
CVE pattern: DataOffset out-of-bounds in SMB T2 response parsing
Finding #5 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 1132 |
| Taint snippet | psx_rsp = (OPEN_PSX_RSP *) |
| Tainted var | psx_rsp |
| Sink | memcpy() line 1153 (arg 1, role=pointer) |
| Sink snippet | memcpy(pRetData, |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in memcpy source within CIFSPOSIXCreate when DataOffset is crafted to place psx_rsp near or beyond packet end
Fix: Validate DataOffset so that (char*)psx_rsp + sizeof(OPEN_PSX_RSP) + sizeof(FILE_UNIX_BASIC_INFO) <= packet_end before performing memcpy. The BCC guard at line 1147 helps but does not bound DataOffset independently.
CVE pattern: DataOffset out-of-bounds in SMB T2 response parsing
CIFSSMBPosixLock() — fs/smb/client/cifssmb.c BUG confidence=high
The function validates data_count >= sizeof(struct cifs_posix_lock) but does not verify that data_offset + sizeof(struct cifs_posix_lock) <= packet_end before constructing parm_data from the server-supplied data_offset. A malicious server can supply a large DataOffset that passes the data_count check yet places parm_data beyond the actual receive buffer, enabling OOB reads. All 7 findings share this single root cause.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->lock_type line 2379 |
| Sink snippet | if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock when server returns a crafted DataOffset placing parm_data beyond the receive buffer
Fix: After computing parm_data, verify that (char*)parm_data + sizeof(struct cifs_posix_lock) <= (char*)pSMBr + total_packet_size. Specifically: validate that data_offset + sizeof(struct cifs_posix_lock) does not exceed the bounds of the received buffer (e.g., check against get_bcc + offsetof(hdr, Protocol) or use validate_t2's returned length).
CVE pattern: CVE-2022-NNNN style DataOffset OOB — unvalidated server-supplied offset used for pointer arithmetic in SMB response parsing
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->lock_type line 2382 |
| Sink snippet | if (parm_data->lock_type == |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock when server returns a crafted DataOffset
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->lock_type line 2385 |
| Sink snippet | else if (parm_data->lock_type == |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock when server returns a crafted DataOffset
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->start line 2389 |
| Sink snippet | pLockData->fl_start = le64_to_cpu(parm_data->start); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock accessing parm_data->start
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #5 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->length line 2391 |
| Sink snippet | (le64_to_cpu(parm_data->length) ? |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock accessing parm_data->length
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #6 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->length line 2392 |
| Sink snippet | le64_to_cpu(parm_data->length) - 1 : 0); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock accessing parm_data->length
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #7 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 2370 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | parm_data |
| Pointer deref | parm_data->pid line 2393 |
| Sink snippet | pLockData->c.flc_pid = -le32_to_cpu(parm_data->pid); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBPosixLock accessing parm_data->pid
Fix: Validate data_offset + sizeof(struct cifs_posix_lock) <= received buffer length before deriving parm_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
CIFSSMBQAllEAs() — fs/smb/client/cifssmb.c MIXED confidence=medium
The function has layered validation but with a critical gap: data_offset from the server is not validated against the packet bounds before ea_response_data is constructed and dereferenced. The end-of-SMB check at line 6226 only runs AFTER already dereferencing ea_response_data->list_len. The per-iteration loop bounds checks (list_len < 0 after subtracting fixed header size 4) are sufficient for protecting name_len/value_len reads since the 4-byte subtraction accounts for the fea fixed-header fields. The memcpy/memcmp operations are generally protected: value_len is checked against buf_size before the copy; name_len is bounded by list_len (already bounded to end_of_smb). The memcpy(EAData, temp_ptr, name_len) at line 6280 has a weaker destination check (accumulated rc vs buf_size) that may be insufficient.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | ea_response_data |
| Pointer deref | ea_response_data->list_len line 6214 |
| Sink snippet | list_len = le32_to_cpu(ea_response_data->list_len); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQAllEAs when server supplies a DataOffset that places ea_response_data->list_len beyond the packet buffer
Fix: Before computing ea_response_data, validate that data_offset + sizeof(struct fealist) <= (size of packet buffer derived from bcc and hdr). Specifically: if (data_offset < sizeof(pSMBr->hdr) || data_offset + sizeof(struct fealist) > (char*)end_of_smb - (char*)&pSMBr->hdr.Protocol) { rc = -EIO; goto QAllEAsOut; }
CVE pattern: CVE-2022-NNNN style DataOffset OOB — server-controlled offset used without pre-dereference bounds check
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | ea_response_data |
| Pointer deref | ea_response_data->list line 6236 |
| Sink snippet | temp_fea = &ea_response_data->list; |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQAllEAs — ea_response_data->list field accessed before offset is validated against packet end
Fix: Same fix as finding #1: validate data_offset + sizeof(struct fealist) fits within packet before computing ea_response_data. The list field is at offset 4 inside fealist, so the same bounds check covers it.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #3 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 6214 |
| Taint snippet | list_len = le32_to_cpu(ea_response_data->list_len); |
| Tainted var | list_len |
| Loop | while_loop line 6238 |
| Sink snippet | while (list_len > 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: list_len is bounded against end_of_smb at line 6226 before the loop. Inside the loop, list_len is decremented by 4 and checked < 0, then decremented by name_len+1+value_len and checked < 0 again. These per-iteration guards ensure the loop terminates before going past end_of_smb. Counterexample attempt: list_len=8 → exits at line 6216. list_len=9, loop: subtract 4 → 5 >= 0 ok, read name_len=5, value_len=0, subtract 6 → -1 < 0 → error exit. No OOB path found.
Finding #4 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | temp_fea |
| Pointer deref | temp_fea->name_len line 6251 |
| Sink snippet | name_len = temp_fea->name_len; |
| Possibly guarded | no |
Dismissed: temp_fea->name_len is read after subtracting 4 from list_len and verifying list_len >= 0 (i.e., at least 4 bytes remain). struct fea has a 1-byte reserved, 1-byte name_len, 2-byte value_len = 4 bytes fixed header, so the 4-byte reservation is exact. temp_fea is derived from temp_ptr which stays within the ea_response_data region (bounded by end_of_smb check). No OOB counterexample constructible.
Finding #5 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | temp_fea |
| Pointer deref | temp_fea->value_len line 6252 |
| Sink snippet | value_len = le16_to_cpu(temp_fea->value_len); |
| Possibly guarded | no |
Dismissed: Same reasoning as finding #4. value_len is at offset 2 within the 4-byte fea header. The 4-byte subtraction and list_len >= 0 check covers both name_len (offset 1) and value_len (offset 2). False positive.
Finding #6 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | name_len |
| Sink | memcmp() line 6262 (arg 2, role=size) |
| Sink snippet | memcmp(ea_name, temp_ptr, name_len) == 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: name_len is an unsigned int derived from temp_fea->name_len (u8, so max 255). The source buffer (temp_ptr) is bounded: list_len check ensures name_len + 1 + value_len <= remaining list_len <= end_of_smb - temp_ptr. For the destination (ea_name), memcmp reads ea_name up to name_len bytes; ea_name is caller-supplied and the comparison is checking equality with ea_name_len == name_len first, so name_len is bounded by ea_name_len which is computed from strlen(ea_name) in the caller. No OOB.
Finding #7 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | temp_ptr |
| Sink | memcmp() line 6262 (arg 1, role=pointer) |
| Sink snippet | memcmp(ea_name, temp_ptr, name_len) == 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: temp_ptr at the memcmp call points to the name field of the current fea entry. The list_len bounds checks ensure temp_ptr + name_len stays within the validated buffer region (bounded by end_of_smb). False positive.
Finding #8 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 6252 |
| Taint snippet | value_len = le16_to_cpu(temp_fea->value_len); |
| Tainted var | value_len |
| Sink | memcpy() line 6271 (arg 2, role=size) |
| Sink snippet | memcpy(EAData, temp_ptr, value_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: Source: value_len bounded by list_len check (name_len+1+value_len <= remaining list_len <= end_of_smb - base). Destination: line 6267 checks (size_t)value_len > buf_size before the memcpy, ensuring EAData has enough space. Both source and destination are checked. False positive.
Finding #9 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | temp_ptr |
| Sink | memcpy() line 6271 (arg 1, role=pointer) |
| Sink snippet | memcpy(EAData, temp_ptr, value_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: temp_ptr at the memcpy call (line 6271) has been advanced by name_len+1. The remaining bytes (value_len) are within the bounds validated by the list_len accounting. False positive.
Finding #10 — Category B — BUG oob_write
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | name_len |
| Sink | memcpy() line 6280 (arg 2, role=size) |
| Sink snippet | memcpy(EAData, temp_ptr, name_len); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQAllEAs — memcpy writes name_len bytes into EAData without checking remaining EAData buffer capacity
Fix: Before the memcpy at line 6280, verify that the current EAData pointer plus the number of bytes to be written (5 + name_len + 1) does not exceed EAData_start + buf_size. The current check 'rc < (int)buf_size' uses the accumulated total, not the remaining space at the current EAData pointer, and may allow an individual name_len copy to overflow if prior entries consumed buffer space.
CVE pattern: Insufficient destination buffer check before memcpy with server-controlled size
Finding #11 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 6210 |
| Taint snippet | data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | temp_ptr |
| Sink | memcpy() line 6280 (arg 1, role=pointer) |
| Sink snippet | memcpy(EAData, temp_ptr, name_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same reasoning as finding #10 revision. temp_ptr is within the validated server buffer region. The destination EAData is protected by the rc < buf_size check which tracks total bytes written. False positive.
CIFSSMBQFSAttributeInfo() — fs/smb/client/cifssmb.c BUG confidence=high
The function calls validate_t2() and checks BCC >= 13, but neither check validates that DataOffset from the server response actually points within the response buffer before using it for pointer arithmetic. A server can supply an arbitrary DataOffset value (up to 65535 as a __u16) causing response_data to point far beyond the pSMBr buffer, leading to an OOB read into the memcpy source argument.
Finding #1 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 5139 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Sink | memcpy() line 5144 (arg 1, role=pointer) |
| Sink snippet | memcpy(&tcon->fsAttrInfo, response_data, |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSAttributeInfo (memcpy reading beyond the response buffer), or kernel panic / info disclosure from reading attacker-controlled memory into tcon->fsAttrInfo
Fix: After extracting data_offset, validate it: ensure data_offset is within the received buffer bounds, i.e., data_offset + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) <= bytes_returned (or the total response buffer size). For example: if (data_offset < sizeof(pSMBr->hdr) || data_offset + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) > (unsigned int)bytes_returned) { rc = -EIO; } else { /* proceed with memcpy */ }
CVE pattern: SMB response DataOffset out-of-bounds read (similar pattern to CVE-2011-1013 and related CIFS DataOffset OOB vulnerabilities)
CIFSSMBQFSDeviceInfo() — fs/smb/client/cifssmb.c BUG confidence=high
The function validates BCC size but not the DataOffset field from the server response. DataOffset is server-supplied via le16_to_cpu() and used directly in pointer arithmetic without any bounds check, allowing a malicious server to craft a DataOffset that points outside the response buffer. The BCC check (line 5208-5212) only verifies the byte count is large enough for the struct, but does NOT validate that DataOffset places response_data within a safe region of the buffer.
Finding #1 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 5214 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Sink | memcpy() line 5219 (arg 1, role=pointer) |
| Sink snippet | memcpy(&tcon->fsDevInfo, response_data, |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSDeviceInfo — memcpy reads sizeof(FILE_SYSTEM_DEVICE_INFO) bytes from an attacker-controlled offset relative to &pSMBr->hdr.Protocol, potentially reading memory far outside the response buffer
Fix: After validate_t2() succeeds, validate data_offset: check that (data_offset + sizeof(FILE_SYSTEM_DEVICE_INFO)) <= bytes_returned (or the actual SMB response buffer length), and that data_offset >= minimum safe offset (e.g., sizeof the SMB header fields), before computing response_data and calling memcpy. For example: if (data_offset < sizeof(pSMBr->hdr) || data_offset + sizeof(FILE_SYSTEM_DEVICE_INFO) > bytes_returned) { rc = -EIO; } else { ... memcpy ... }
CVE pattern: CVE-2022-NNNN style DataOffset OOB — server-controlled DataOffset used in pointer arithmetic without upper-bound validation before memcpy, similar to multiple historical CIFS/SMB DataOffset OOB vulnerabilities
CIFSSMBQFSInfo() — fs/smb/client/cifssmb.c BUG confidence=high
The function calls validate_t2() and checks BCC >= 24, but neither check validates the server-supplied DataOffset field. validate_t2() verifies the TRANSACTION2 response structure is minimally sane, but does not bound-check DataOffset against the packet end. The BCC check (>= 24) ensures the byte count field says there are at least 24 bytes of data, but the actual data_offset value (from pSMBr->t2.DataOffset) could point anywhere in or beyond the packet buffer. A malicious or malformed server could supply a DataOffset that places response_data outside the received packet, leading to an out-of-bounds read when fields like BytesPerSector, SectorsPerAllocationUnit, TotalAllocationUnits, and AvailableAllocationUnits are accessed.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5048 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->BytesPerSector line 5055 |
| Sink snippet | le32_to_cpu(response_data->BytesPerSector) * |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSInfo reading beyond response buffer when accessing response_data->BytesPerSector
Fix: After computing data_offset, validate that data_offset + sizeof(FILE_SYSTEM_SIZE_INFO) <= bytes_returned (or total packet size). Specifically: if (data_offset > bytes_returned || bytes_returned - data_offset < sizeof(FILE_SYSTEM_SIZE_INFO)) { rc = -EIO; } before using response_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB in SMB TRANSACTION2 response
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5048 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->SectorsPerAllocationUnit line 5056 |
| Sink snippet | le32_to_cpu(response_data-> |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSInfo reading beyond response buffer when accessing response_data->SectorsPerAllocationUnit
Fix: After computing data_offset, validate that data_offset + sizeof(FILE_SYSTEM_SIZE_INFO) <= bytes_returned before dereferencing response_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB in SMB TRANSACTION2 response
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5048 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->TotalAllocationUnits line 5066 |
| Sink snippet | le64_to_cpu(response_data->TotalAllocationUnits); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSInfo reading beyond response buffer when accessing response_data->TotalAllocationUnits
Fix: After computing data_offset, validate that data_offset + sizeof(FILE_SYSTEM_SIZE_INFO) <= bytes_returned before dereferencing response_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB in SMB TRANSACTION2 response
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5048 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->AvailableAllocationUnits line 5068 |
| Sink snippet | le64_to_cpu(response_data->AvailableAllocationUnits); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSInfo reading beyond response buffer when accessing response_data->AvailableAllocationUnits
Fix: After computing data_offset, validate that data_offset + sizeof(FILE_SYSTEM_SIZE_INFO) <= bytes_returned before dereferencing response_data.
CVE pattern: CVE-2022-NNNN style DataOffset OOB in SMB TRANSACTION2 response
CIFSSMBQFSPosixInfo() — fs/smb/client/cifssmb.c VALIDATE confidence=medium
The function calls validate_t2() before using DataOffset, and checks BCC >= 13. Whether validate_t2() sufficiently validates DataOffset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet_end determines if these are real bugs. The BCC >= 13 check alone does not bound DataOffset arithmetic. Standard CIFS validate_t2() implementations do check DataOffset against smb_buf_length, which would make these false positives; without that source, confidence is medium.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->BlockSize line 5441 |
| Sink snippet | le32_to_cpu(response_data->BlockSize); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSPosixInfo if validate_t2 does not bound DataOffset+sizeof(FILE_SYSTEM_POSIX_INFO) within packet
Fix: After validate_t2() and BCC check, also verify that data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= smb_buf_length(pSMBr) before casting response_data pointer
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->TotalBlocks line 5450 |
| Sink snippet | le64_to_cpu(response_data->TotalBlocks); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading TotalBlocks field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->BlocksAvail line 5452 |
| Sink snippet | le64_to_cpu(response_data->BlocksAvail); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading BlocksAvail field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->UserBlocksAvail line 5453 |
| Sink snippet | if (response_data->UserBlocksAvail == cpu_to_le64(-1)) { |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading UserBlocksAvail field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #5 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->UserBlocksAvail line 5457 |
| Sink snippet | le64_to_cpu(response_data->UserBlocksAvail); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading UserBlocksAvail field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #6 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->TotalFileNodes line 5459 |
| Sink snippet | if (response_data->TotalFileNodes != cpu_to_le64(-1)) |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading TotalFileNodes field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #7 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->TotalFileNodes line 5461 |
| Sink snippet | le64_to_cpu(response_data->TotalFileNodes); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading TotalFileNodes field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #8 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->FreeFileNodes line 5462 |
| Sink snippet | if (response_data->FreeFileNodes != cpu_to_le64(-1)) |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading FreeFileNodes field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
Finding #9 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 5435 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->FreeFileNodes line 5464 |
| Sink snippet | le64_to_cpu(response_data->FreeFileNodes); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds reading FreeFileNodes field beyond packet boundary
Fix: Validate data_offset + sizeof(FILE_SYSTEM_POSIX_INFO) <= packet size before dereferencing response_data
CVE pattern: CVE-2022 style DataOffset OOB in SMB TRANSACTION2 response parsing
CIFSSMBQFSUnixInfo() — fs/smb/client/cifssmb.c BUG confidence=high
The function sends a request, receives a response, and calls validate_t2() plus a BCC size check before using DataOffset. However, validate_t2() only checks that the T2 response is structurally valid (parameter/data offsets fit within the SMB buffer), and the BCC check (< 13) only verifies minimum data size. Neither check bounds pSMBr->t2.DataOffset against the actual response buffer before using it in pointer arithmetic to derive response_data. A server-supplied DataOffset that points beyond the response buffer would cause an OOB read via memcpy().
Finding #1 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 5286 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Sink | memcpy() line 5291 (arg 1, role=pointer) |
| Sink snippet | memcpy(&tcon->fsUnixInfo, response_data, |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in CIFSSMBQFSUnixInfo; kernel crash or memory corruption when memcpy reads beyond the response buffer using a crafted DataOffset value
Fix: After reading data_offset, validate that data_offset is within the received buffer: check that (data_offset + sizeof(FILE_SYSTEM_UNIX_INFO)) <= (bytes_returned - offsetof(TRANSACTION2_QFSI_RSP, hdr.Protocol)) before computing response_data and calling memcpy(). Also ensure data_offset >= offsetof(TRANSACTION2_QFSI_RSP, t2) to avoid pointing before the T2 data area.
CVE pattern: SMB T2 response DataOffset out-of-bounds read — similar pattern to CVE-2011-1090 and related CIFS T2 DataOffset OOB vulnerabilities
CIFSSMBRead() — fs/smb/client/cifssmb.c MIXED confidence=high
pSMBr is the server response buffer. data_length is properly bounded against CIFSMaxBufSize and count (finding #1 is a false positive). However, DataOffset (pSMBr->DataOffset) used in pointer arithmetic to compute pReadData is not bounds-checked against the response buffer size, allowing a malicious server to set DataOffset such that pReadData points before or beyond the response buffer, causing an OOB read (finding #2 is a real bug).
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1717 |
| Taint snippet | int data_length = le16_to_cpu(pSMBr->DataLengthHigh); |
| Tainted var | data_length |
| Sink | memcpy() line 1738 (arg 2, role=size) |
| Sink snippet | memcpy(*buf, pReadData, data_length); |
| Possibly guarded | yes (heuristic) |
Dismissed: data_length is checked at line 1723-1724: it must be <= CIFSMaxBufSize AND <= count (the requested byte count). CIFSMaxBufSize is a compile-time constant bounding the source buffer, and count is the caller-supplied maximum. A counterexample would require data_length > CIFSMaxBufSize or data_length > count — both are blocked by the guard. The destination buffer *buf was allocated by the caller for at least 'count' bytes, so the destination side is also covered. No counterexample exists that passes both guards and still overflows.
Finding #2 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 1731 |
| Taint snippet | pReadData = (char *) (&pSMBr->hdr.Protocol) + |
| Tainted var | pReadData |
| Sink | memcpy() line 1738 (arg 1, role=pointer) |
| Sink snippet | memcpy(*buf, pReadData, data_length); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds in CIFSSMBRead — a malicious server sets DataOffset to a very large value, pushing pReadData beyond the response buffer; the subsequent memcpy reads out-of-bounds kernel heap memory into the caller's buffer.
Fix: Before computing pReadData, validate that DataOffset is within the received response buffer: the offset from &pSMBr->hdr.Protocol to the end of the buffer must be at least DataOffset + data_length. Concretely: u16 data_offset = le16_to_cpu(pSMBr->DataOffset); if (data_offset < sizeof(READ_RSP) - offsetof(READ_RSP, hdr.Protocol) || (size_t)data_offset + data_length > rsp_iov.iov_len - offsetof(READ_RSP, hdr.Protocol)) { rc = -EIO; *nbytes = 0; } else { pReadData = (char *)(&pSMBr->hdr.Protocol) + data_offset; ... }
CVE pattern: SMB response DataOffset out-of-bounds read — similar in structure to CVE-2019-5544 / historical CIFS DataOffset validation issues
CIFSSMBUnixQuerySymLink() — fs/smb/client/cifssmb.c MIXED confidence=medium
The function validates the response with validate_t2() and checks BCC >= 2, but does not validate that DataOffset is within the response buffer bounds before computing data_start, nor does it validate that DataCount is within the actual received data. Finding #1 (data_start OOB pointer) is a real concern but the actual dangerous sink in cifs_strndup_from_utf16 uses 'count' (maxlen) as the bound for cifs_utf16_bytes/kstrndup, not data_start itself as a size — so data_start affects the src pointer, not allocation size directly. Finding #2 (count used as maxlen) is genuine: a server-supplied DataCount is passed as maxlen to cifs_strndup_from_utf16 which uses it in cifs_utf16_bytes and kstrndup without verifying it fits within the actual received buffer, but the allocation itself is bounded by the actual UTF-16 byte scan result, so the risk is an OOB read of src rather than an oversized allocation.
Finding #1 — Category B — cross-function via cifs_strndup_from_utf16() — BUG oob_read
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 2939 |
| Taint snippet | data_start = ((char *) &pSMBr->hdr.Protocol) + |
| Tainted var | data_start |
| Call site | line 2948 — passes data_start to cifs_strndup_from_utf16() |
| Call snippet | *symlinkinfo = cifs_strndup_from_utf16(data_start, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds or kernel panic if server supplies a DataOffset that points outside the response buffer, causing data_start to point to unrelated memory that is then scanned by cifs_utf16_bytes or kstrndup
Fix: After computing data_start, validate that data_start + count does not exceed the end of the received response buffer (pSMBr + bytes_returned or similar bound). Reject the response with -EIO if the range is out of bounds.
CVE pattern: CVE-2022-NNNN style DataOffset OOB — server-controlled offset used without bounds check before pointer arithmetic on response buffer
Finding #2 — Category B — cross-function via cifs_strndup_from_utf16() — BUG oob_read
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 2937 |
| Taint snippet | u16 count = le16_to_cpu(pSMBr->t2.DataCount); |
| Tainted var | count |
| Call site | line 2948 — passes count to cifs_strndup_from_utf16() |
| Call snippet | *symlinkinfo = cifs_strndup_from_utf16(data_start, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds when cifs_utf16_bytes or kstrndup reads up to 'count' bytes from data_start, which may exceed the actual received data if count > actual data in buffer
Fix: Validate that count (DataCount) does not exceed bytes_returned minus the offset to data_start (i.e., the actual data region in the response). Clamp or reject with -EIO if count > available bytes. The check 'get_bcc(&pSMBr->hdr) < 2' is insufficient — it only ensures BCC is at least 2, not that DataCount fits within the buffer.
CVE pattern: Missing DataCount bounds validation against actual response buffer size in CIFS/SMB symlink query — similar to several historical CIFS CVEs involving unchecked server-supplied counts
SMBOldQFSInfo() — fs/smb/client/cifssmb.c BUG confidence=high
The function validates that BCC >= 18 (line 4954) but does NOT validate that data_offset (server-supplied from pSMBr->t2.DataOffset) places the FILE_SYSTEM_ALLOC_INFO struct within the actual received packet. A malicious or buggy server can supply a large DataOffset that pushes response_data beyond the packet buffer, causing out-of-bounds reads. The BCC check only validates the byte count field, not the DataOffset field. validate_t2() is called but its postcondition does not guarantee DataOffset+sizeof(FILE_SYSTEM_ALLOC_INFO) <= packet_end. Counterexample: data_offset = 0xFFFF would place response_data far beyond the packet, yet the only guard is BCC >= 18.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4958 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->BytesPerSector line 4965 |
| Sink snippet | le16_to_cpu(response_data->BytesPerSector) * |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in SMBOldQFSInfo when accessing response_data->BytesPerSector; kernel crash or info disclosure from heap memory beyond the received SMB response buffer
Fix: After computing response_data, verify that (char*)response_data + sizeof(FILE_SYSTEM_ALLOC_INFO) <= (char*)&pSMBr->hdr.Protocol + get_bcc(&pSMBr->hdr) + sizeof(pSMBr->hdr) (i.e., within the packet bounds). Also validate data_offset is not smaller than the header size. For example: if (data_offset > bytes_returned - sizeof(*response_data)) { rc = -EIO; goto done; }
CVE pattern: SMB transaction2 DataOffset out-of-bounds read — similar pattern to CVE-2012-1090 style CIFS DataOffset OOB
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4958 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->SectorsPerAllocationUnit line 4966 |
| Sink snippet | le32_to_cpu(response_data-> |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in SMBOldQFSInfo when accessing response_data->SectorsPerAllocationUnit; kernel crash or info disclosure
Fix: Same fix as finding #1: validate data_offset + sizeof(FILE_SYSTEM_ALLOC_INFO) <= received packet length before dereferencing response_data.
CVE pattern: SMB transaction2 DataOffset out-of-bounds read
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4958 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->TotalAllocationUnits line 4976 |
| Sink snippet | le32_to_cpu(response_data->TotalAllocationUnits); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in SMBOldQFSInfo when accessing response_data->TotalAllocationUnits; kernel crash or info disclosure
Fix: Same fix as finding #1.
CVE pattern: SMB transaction2 DataOffset out-of-bounds read
Finding #4 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 4958 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | response_data |
| Pointer deref | response_data->FreeAllocationUnits line 4978 |
| Sink snippet | le32_to_cpu(response_data->FreeAllocationUnits); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in SMBOldQFSInfo when accessing response_data->FreeAllocationUnits; kernel crash or info disclosure
Fix: Same fix as finding #1.
CVE pattern: SMB transaction2 DataOffset out-of-bounds read
cifs_create_reparse_inode() — fs/smb/client/cifssmb.c MIXED confidence=medium
The xattr_iov data appears to be kernel-constructed for EA setting during reparse point creation, making finding #1 likely a false positive. However, CIFSSMBSetEA() has an explicitly acknowledged missing bounds check (the 'BB' comment) for ea_value_len against the negotiated SMB buffer size, making finding #2 a real concern if the EA value length can be attacker-influenced.
Finding #1 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 3156 |
| Taint snippet | ea = (struct smb2_file_full_ea_info *)((u8 *)ea + |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 3157 |
| Sink snippet | le32_to_cpu(ea->next_entry_offset)); |
| Possibly guarded | no |
Dismissed: The xattr_iov is kernel-constructed (passed from internal reparse point creation logic, not a server response). The next_entry_offset field is written by the kernel when constructing the EA context. The check for == 0 is sufficient for kernel-controlled data. This is a false positive because the EA context structure is built locally, not received from the server.
Finding #2 — Category B — cross-function via CIFSSMBSetEA() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 3144 |
| Taint snippet | rc = CIFSSMBSetEA(xid, |
| Tainted var | le16_to_cpu(ea->ea_value_length) |
| Call site | line 3144 — passes le16_to_cpu(ea->ea_value_length) to CIFSSMBSetEA() |
| Call snippet | rc = CIFSSMBSetEA(xid, |
| Sink (in callee) | memcpy() line 6390 (arg 2, role=size) |
| Sink snippet | memcpy(parm_data->list.name + name_len + 1, |
| Possibly guarded | no |
Dismissed: ea->ea_value_length comes from xattr_iov which is kernel-constructed data passed in by the caller, not a server response. The memcpy in CIFSSMBSetEA() has an acknowledged TODO comment about adding a length check, but since the EA value length is kernel-controlled (not server-supplied), this is not an exploitable vulnerability from a network attacker perspective. The missing bound check against the negotiated SMB buffer size is a robustness issue but not a security bug in this call path. Counterexample: no concrete counterexample possible since the caller controls ea_value_length.
cifs_do_get_acl() — fs/smb/client/cifssmb.c MIXED confidence=medium
Finding #1 concerns data_offset used to compute src pointer without validating it against the received buffer size, which could produce an out-of-bounds pointer before cifs_to_posix_acl() even begins. Finding #2 is a false positive: the count used in ace_array[count] is cifs_acl->access_entry_count (a callee-local variable), and it IS validated against size_of_data_area before the subscript, making the access safe.
Finding #1 — Category C — cross-function via cifs_to_posix_acl() — BUG oob_read
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 3521 |
| Taint snippet | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); |
| Tainted var | data_offset |
| Call site | line 3523 — passes data_offset to cifs_to_posix_acl() |
| Call snippet | rc = cifs_to_posix_acl(acl, |
| Subscript (in callee) | [] line 3343 |
| Sink snippet | pACE = &cifs_acl->ace_array[count]; |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds or kernel panic when dereferencing src pointer derived from unchecked server-supplied DataOffset
Fix: Validate data_offset before use: check that data_offset + count <= total response buffer size (e.g., bytes_returned or get_bcc result), rejecting the response if the data area falls outside the received buffer.
CVE pattern: CVE-2022-NNNN style DataOffset OOB — unchecked DataOffset in SMB/CIFS transaction2 response
Finding #2 — Category C — cross-function via cifs_to_posix_acl() — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 3522 |
| Taint snippet | __u16 count = le16_to_cpu(pSMBr->t2.DataCount); |
| Tainted var | count |
| Call site | line 3523 — passes count to cifs_to_posix_acl() |
| Call snippet | rc = cifs_to_posix_acl(acl, |
| Subscript (in callee) | [] line 3343 |
| Sink snippet | pACE = &cifs_acl->ace_array[count]; |
| Possibly guarded | no |
Dismissed: The scanner incorrectly attributes the subscript to the DataCount parameter. Inside cifs_to_posix_acl(), the array subscript &cifs_acl->ace_array[count] uses a local 'count' derived from cifs_acl->access_entry_count, which is validated against size_of_data_area (the DataCount) before the subscript. The check 'if (size_of_data_area < size) return -EINVAL' at line 3333 covers this. No counterexample possible: if count*sizeof(cifs_posix_ace)+header > size_of_data_area, the function returns -EINVAL before reaching line 3343.
cifs_query_reparse_point() — fs/smb/client/cifssmb.c BUG confidence=medium
The function validates data_offset <= 512 and the original data_count <= 2048, and checks start < end. However, it then reassigns data_count from io_rsp->ByteCount and checks ByteCount >= sizeof(*buf), but never verifies that start + sizeof(*buf) <= end (the BCC-derived packet boundary). A server could supply a small BCC but large ByteCount, causing buf->ReparseDataLength to be read beyond packet bounds.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 3032 |
| Taint snippet | data_offset = le32_to_cpu(io_rsp->DataOffset); |
| Tainted var | buf |
| Pointer deref | buf->ReparseDataLength line 3072 |
| Sink snippet | data_count < le16_to_cpu(buf->ReparseDataLength) + len) { |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in cifs_query_reparse_point when accessing buf->ReparseDataLength beyond packet boundary
Fix: Before accessing buf->ReparseDataLength, verify that (start + sizeof(*buf)) <= end, i.e., add: if ((end - start) < (ptrdiff_t)len) { rc = -EIO; goto error; }
CVE pattern: CVE-2022-NNNN style DataOffset OOB — server-controlled offset used to derive pointer without full size validation against packet boundary
Finding #2 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 3032 |
| Taint snippet | data_offset = le32_to_cpu(io_rsp->DataOffset); |
| Tainted var | buf |
| Pointer deref | buf->ReparseDataLength line 3074 |
| Sink snippet | data_count, le16_to_cpu(buf->ReparseDataLength) + len); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in cifs_query_reparse_point when accessing buf->ReparseDataLength in error trace path beyond packet boundary
Fix: Same fix as finding #1: validate (end - start) >= sizeof(*buf) before dereferencing buf fields.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
Finding #3 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 3032 |
| Taint snippet | data_offset = le32_to_cpu(io_rsp->DataOffset); |
| Tainted var | buf |
| Pointer deref | buf->ReparseTag line 3078 |
| Sink snippet | *tag = le32_to_cpu(buf->ReparseTag); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in cifs_query_reparse_point when accessing buf->ReparseTag beyond packet boundary
Fix: Same fix: ensure (end - start) >= sizeof(*buf) before line 3069. Also the check at 3071 using ByteCount rather than the BCC-derived boundary must be supplemented with an end-boundary check.
CVE pattern: CVE-2022-NNNN style DataOffset OOB
cifs_to_posix_acl() — fs/smb/client/cifssmb.c FP confidence=medium
The function validates buffer bounds before actual memory reads occur. The subscript use at line 3343 is pointer arithmetic (address computation), not a memory read. The bounds check at line 3347 covers the full range (sizeof(cifs_posix_acl) + access_entry_count*ace_size + default_entry_count*ace_size) before any data is actually read from pACE in the FOREACH loop. However, there is a latent integer overflow risk in the size calculation with large server-supplied counts that could theoretically cause the bounds check to pass incorrectly.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 3339 |
| Taint snippet | count = le16_to_cpu(cifs_acl->access_entry_count); |
| Tainted var | count |
| Subscript | [] line 3343 |
| Sink snippet | pACE = &cifs_acl->ace_array[count]; |
| Possibly guarded | no |
Dismissed: The pointer arithmetic at line 3343 is address computation, not a memory read. Actual memory reads only occur in the FOREACH loop at line 3359, which is after the bounds check at line 3347 validates the full buffer range. No counterexample found where the bounds check passes but OOB access occurs, given non-overflowing arithmetic. The check at line 3347 comes after pACE is computed but before pACE is dereferenced, so it is sufficient for memory safety. A potential integer overflow in the size multiplication with very large server-supplied counts (e.g., count=65535) could in theory defeat the check, but this is a separate concern from the flagged subscript issue.
parse_dfs_referrals() — fs/smb/client/misc.c FP confidence=high
parse_dfs_referrals() has solid validation discipline: it checks rsp_size against sizeof(*rsp), then against sizeof(*rsp)+num_referrals*sizeof(REFERRAL3), bounds-checks DfsPathOffset and NetworkAddressOffset against data_end before dereferencing, and bounds-checks path_consumed against search_name_utf16_len before passing to cifs_utf16_bytes(). All three flagged flows are protected by guards that are tight enough to prevent exploitation.
Finding #1 — Category F — cross-function via cifs_utf16_bytes() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 757 |
| Taint snippet | path_consumed = le16_to_cpu(rsp->PathConsumed); |
| Tainted var | path_consumed |
| Call site | line 774 — passes path_consumed to cifs_utf16_bytes() |
| Call snippet | node->path_consumed = cifs_utf16_bytes(tmp, path_consumed, |
| Loop | for_loop line 299 |
| Sink snippet | for (i = 0; i < maxwords; i++) { |
| Possibly guarded | yes (heuristic) |
Dismissed: path_consumed is validated at line 762: it must not exceed search_name_utf16_len (= search_name_len*2+2), and tmp is allocated with exactly search_name_utf16_len bytes. cifs_utf16_bytes iterates up to path_consumed/2 words of tmp, which is within the allocated size. No counterexample can be constructed: any path_consumed > search_name_utf16_len causes early return before the call.
Finding #2 — Category B — cross-function via cifs_strndup_from_utf16() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 794 |
| Taint snippet | temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); |
| Tainted var | temp |
| Call site | line 796 — passes temp to cifs_strndup_from_utf16() |
| Call snippet | node->path_name = cifs_strndup_from_utf16(temp, max_len, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | no |
Dismissed: The scanner misattributes the taint: temp is a pointer derived from ref+DfsPathOffset, not a size. The actual kmalloc size inside cifs_strndup_from_utf16 is computed by cifs_utf16_bytes scanning at most max_len bytes (= data_end - temp, always non-negative after the offset guard). The guard at line 790 ensures offset does not exceed data_end - ref, so temp <= data_end and max_len >= 0 and bounded by rsp_size.
Finding #3 — Category B — cross-function via cifs_strndup_from_utf16() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 794 |
| Taint snippet | temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); |
| Tainted var | max_len |
| Call site | line 796 — passes max_len to cifs_strndup_from_utf16() |
| Call snippet | node->path_name = cifs_strndup_from_utf16(temp, max_len, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | no |
Dismissed: max_len = data_end - temp is bounded by rsp_size (an externally validated response size parameter). The guard at line 790 ensures DfsPathOffset <= data_end - ref, making max_len non-negative and at most rsp_size. cifs_utf16_bytes uses max_len only as an iteration bound over a received buffer, and the kmalloc size is derived from actual content length, not max_len directly. No counterexample exists given these constraints.
cnvrtDosUnixTm() — fs/smb/client/netmisc.c FP confidence=high
The function processes server-supplied DOS date/time fields. While the values are genuinely server-supplied (tainted), the code at lines 181-184 validates month with an explicit bounds check (month < 1 || month > 12) and then clamps it to [1,12] via clamp(). After the mandatory month -= 1 decrement at line 186, the index is guaranteed to be in [0,11], which is a valid range for total_days_of_prev_months. The static scanner incorrectly flagged this as unguarded; the clamp provides sufficient protection.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 164 |
| Taint snippet | u16 date = le16_to_cpu(le_date); |
| Tainted var | month |
| Subscript | [] line 187 |
| Sink snippet | days = day + total_days_of_prev_months[month]; |
| Possibly guarded | no |
Dismissed: The scanner marked this as 'Possibly guarded: no' but missed the clamp() call at line 183-184. After clamp(month, 1, 12), month is in [1,12]; after month -= 1, the array subscript is in [0,11]. No counterexample can be constructed: any server-supplied month value, whether 0, negative (impossible since u16 fields via sd->Month are unsigned), or >12, will be clamped before use. The array access is fully safe.
mknod_wsl() — fs/smb/client/reparse.c FP confidence=high
Both findings are false positives. Finding #1: cc = xattr_iov.iov_base is populated by wsl_set_xattrs(), a kernel-side builder function that constructs xattr data locally — DataLength is written by the kernel with cpu_to_le32(constant), so le32_to_cpu() recovers a locally-controlled value, not server-supplied data. Finding #2: cifs_free_open_info() calls memset(data, 0, sizeof(*data)) where the size argument is sizeof(*data), a compile-time constant derived from the struct type — the scanner incorrectly traced taint through the struct pointer to the sizeof operator, but sizeof is evaluated at compile time and is independent of any field value in the struct.
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 703 |
| Taint snippet | len = le32_to_cpu(cc->ctx.DataLength); |
| Tainted var | len |
| Sink | memcpy() line 704 (arg 2, role=size) |
| Sink snippet | memcpy(data.wsl.eas, &cc->ea, len); |
| Possibly guarded | no |
Dismissed: cc is set from xattr_iov.iov_base, which is populated by wsl_set_xattrs() — a locally-constructed kernel buffer. The DataLength field is written by the kernel (cpu_to_le32 of a constant), not received from a server. le32_to_cpu() here is simply recovering a kernel-written value. This matches false positive pattern #1 (locally-written struct fields). No server-supplied data is involved.
Finding #2 — Category B — cross-function via cifs_free_open_info() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 696 |
| Taint snippet | data = (struct cifs_open_info_data) { |
| Tainted var | data |
| Call site | line 715 — passes data to cifs_free_open_info() |
| Call snippet | cifs_free_open_info(&data); |
| Sink (in callee) | memset() line 490 (arg 2, role=size) |
| Sink snippet | memset(data, 0, sizeof(*data)); |
| Possibly guarded | no |
Dismissed: The scanner traced taint from le32_to_cpu() at line 696 through the 'data' struct to the memset() in cifs_free_open_info(). However, memset(data, 0, sizeof(*data)) uses sizeof(*data) as its size argument — a compile-time constant representing the size of struct cifs_open_info_data. No tainted field value is used as the size. The scanner incorrectly identified sizeof(*data) as tainted because 'data' itself was considered tainted. This is a clear false positive due to taint propagation through a pointer that is then used only in sizeof().
parse_reparse_native_symlink() — fs/smb/client/reparse.c FP confidence=high
parse_reparse_native_symlink() validates both offs and len against plen before calling smb2_parse_native_symlink(). The check 'offs + 20 > plen || offs + len + 20 > plen' ensures the buffer pointer and length are within bounds. Inside smb2_parse_native_symlink(), the allocation sizes (abs_path_len, smb_target_len) are derived from strlen() of strings produced by UTF-16 conversion of the bounded buffer — they are not directly server-controlled sizes. The scanner is tracking taint through multiple transformations and incorrectly flagging allocations whose sizes are bounded by strlen() of processed strings. No counterexample can be constructed: the guards are tight enough that no combination of offs/len values can pass the check and cause OOB.
Finding #1 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1013 |
| Taint snippet | offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | offs |
| Call site | line 1021 — passes offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | kmalloc() line 939 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(symlinkroot_len + 1 + abs_path_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: offs is validated: offs+20<=plen and offs+len+20<=plen. In callee, abs_path_len=strlen(abs_path)+1 is derived from strlen of UTF-16-converted string of bounded length len, so kmalloc size is safe. No counterexample exists.
Finding #2 — Category A — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 1013 |
| Taint snippet | offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | offs |
| Call site | line 1021 — passes offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 944 (arg 0, role=pointer) |
| Sink snippet | memcpy(linux_target, symroot, symlinkroot_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 944 copies symroot (local config string) into linux_target which was allocated to fit it. offs taint does not affect this operation's safety. Bounds check is sufficient.
Finding #3 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1013 |
| Taint snippet | offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | offs |
| Call site | line 1021 — passes offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 946 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + symlinkroot_len + 1, abs_path, abs_path_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 946 uses abs_path_len=strlen(abs_path)+1 as size and linux_target was allocated with symlinkroot_len+1+abs_path_len bytes. Size matches allocation. No OOB possible.
Finding #4 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1013 |
| Taint snippet | offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | offs |
| Call site | line 1021 — passes offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | kmalloc() line 964 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: kmalloc at line 964 uses levels*3+smb_target_len where smb_target_len=strlen(smb_target)+1. levels is bounded by strlen(full_path) which is a local kernel path, not server data. No counterexample exists.
Finding #5 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1013 |
| Taint snippet | offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | offs |
| Call site | line 1021 — passes offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 974 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 974 copies smb_target+1 of size smb_target_len into linux_target+levels*3; allocation was levels*3+smb_target_len. Sizes match exactly. No OOB possible.
Finding #6 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1014 |
| Taint snippet | len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | len |
| Call site | line 1021 — passes len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | kmalloc() line 939 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(symlinkroot_len + 1 + abs_path_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: len is validated: offs+len+20<=plen ensures the UTF-16 string of length len is within the buffer. abs_path_len from strlen of converted string is bounded by len. kmalloc size is safe.
Finding #7 — Category A — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 1014 |
| Taint snippet | len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | len |
| Call site | line 1021 — passes len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 944 (arg 0, role=pointer) |
| Sink snippet | memcpy(linux_target, symroot, symlinkroot_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 944 copies symroot into linux_target; len taint does not affect this operation. linux_target was allocated to contain symroot. Safe.
Finding #8 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1014 |
| Taint snippet | len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | len |
| Call site | line 1021 — passes len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 946 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + symlinkroot_len + 1, abs_path, abs_path_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: abs_path_len=strlen(abs_path)+1 is bounded by the UTF-16 decoded string length which is bounded by len. memcpy size matches strlen result. Allocation matches. No counterexample.
Finding #9 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1014 |
| Taint snippet | len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | len |
| Call site | line 1021 — passes len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | kmalloc() line 964 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: smb_target_len=strlen(smb_target)+1 bounded by decoded string length. levels from strlen(full_path) is local. kmalloc size safe. No counterexample.
Finding #10 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1014 |
| Taint snippet | len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | len |
| Call site | line 1021 — passes len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(&data->symlink_target, |
| Sink (in callee) | memcpy() line 974 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 974 size is smb_target_len=strlen(smb_target)+1 and allocation was levels*3+smb_target_len. Destination has enough space. No OOB possible.
parse_reparse_nfs() — fs/smb/client/reparse.c FP confidence=high
parse_reparse_nfs() has layered validation: it checks ReparseDataLength >= sizeof(InodeType), subtracts InodeType size, then for the symlink case checks len > 0 and len is even, plus a UniStrnlen null-byte check before calling cifs_strndup_from_utf16(). The len value passed to cifs_strndup_from_utf16() as maxlen is server-supplied but properly guarded. Inside cifs_strndup_from_utf16(), the allocation size is computed from cifs_utf16_bytes() which is bounded by maxlen, not exceeding it, so no integer overflow or undersized allocation is possible.
Finding #1 — Category B — cross-function via cifs_strndup_from_utf16() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 748 |
| Taint snippet | len = le16_to_cpu(buf->ReparseDataLength); |
| Tainted var | len |
| Call site | line 771 — passes len to cifs_strndup_from_utf16() |
| Call snippet | data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: After subtracting sizeof(InodeType) and verifying len > 0 and len is even, the remaining len is a valid maxlen for cifs_strndup_from_utf16(). Inside that callee, kmalloc size = cifs_utf16_bytes(..., maxlen, ...) + nls_nullsize, where cifs_utf16_bytes scans at most maxlen bytes of UTF-16 and returns a byte count for the UTF-8 representation. No counterexample found: any len passing the guards (even, >= 2, <= 65527 after subtraction) yields a valid kmalloc size well within int and size_t ranges. The scanner flagged server-supplied taint flowing into kmalloc, but the intermediate guards are sufficient.
parse_reparse_wsl_symlink() — fs/smb/client/reparse.c FP confidence=high
The function properly validates len > data_offset before computing symname_utf8_len. Since ReparseDataLength is a u16 (max 65535), symname_utf8_len is at most ~65530, and multiplying by 2 gives at most ~131060 — well within int and size_t ranges, so no integer overflow is possible. The symname_utf16 pointer taint is a tracker artifact (it's a locally-allocated buffer). The symname_utf16_len value is bounded by utf8s_to_utf16s() output constraints and the u16 source limit. All three findings are false positives.
Finding #1 — Category B — INTEGER OVERFLOW — false positive
| Category | Cat B — integer overflow: symname_utf8_len * 2 |
|---|---|
| Taint source | le16_to_cpu() line 1033 |
| Taint snippet | int len = le16_to_cpu(buf->ReparseDataLength); |
| Tainted var | symname_utf8_len |
| Overflow expr | symname_utf8_len * 2 |
| Safe fix | kmalloc_array() or check_mul_overflow() |
| Sink | kzalloc() line 1066 (arg 0, role=size_mul_overflow) |
| Sink snippet | symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: symname_utf8_len derives from a u16 field (max 65535). After the guard 'len > data_offset', symname_utf8_len is at most 65535. The multiplication symname_utf8_len * 2 in int arithmetic yields at most 131070, which cannot overflow a 32-bit int or produce a nonsensical size_t. Counterexample: no u16 value can make this overflow. False positive.
Finding #2 — Category B — cross-function via cifs_strndup_from_utf16() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1033 |
| Taint snippet | int len = le16_to_cpu(buf->ReparseDataLength); |
| Tainted var | symname_utf16 |
| Call site | line 1078 — passes symname_utf16 to cifs_strndup_from_utf16() |
| Call snippet | data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | no |
Dismissed: symname_utf16 is a locally allocated pointer (result of kzalloc). The taint tracker incorrectly propagates taint to the pointer variable itself. The pointer points to kernel-controlled memory. This is a taint tracker artifact — false positive.
Finding #3 — Category B — cross-function via cifs_strndup_from_utf16() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1033 |
| Taint snippet | int len = le16_to_cpu(buf->ReparseDataLength); |
| Tainted var | symname_utf16_len |
| Call site | line 1078 — passes symname_utf16_len to cifs_strndup_from_utf16() |
| Call snippet | data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16, |
| Sink (in callee) | kmalloc() line 341 (arg 0, role=size) |
| Sink snippet | dst = kmalloc(len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: symname_utf16_len comes from utf8s_to_utf16s() return value (number of u16 items written, bounded by the output buffer size symname_utf8_len), then doubled. Since symname_utf8_len ≤ 65535, symname_utf16_len ≤ 131070. In cifs_strndup_from_utf16, the resulting kmalloc size is bounded by cifs_utf16_bytes() output (≤ maxlen) plus nls_nullsize. No overflow possible. Counterexample: no u16 source value produces an overflow. False positive.
map_smb_to_linux_error() — fs/smb/client/smb1maperror.c FP confidence=high
The taint propagation is incorrect. `map` is a pointer returned by `search_ntstatus_to_dos_map()`, a lookup function that searches a kernel-internal static table using the server-supplied NT status code as a key. The returned pointer points to kernel static data (compile-time constants), not to the server's network buffer. The server controls only which table entry is selected (or whether NULL is returned), not the contents of `dos_class` or `dos_code` within those entries. The truncation of these kernel-constant values to u8/u16 is intentional and safe because the table entries were designed with those field widths in mind.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le32_to_cpu() line 129 |
| Taint snippet | smberrclass = map->dos_class; |
| Tainted var | smberrclass |
| Truncation | line 129: 32 → 8-bit u8 |
| Sink snippet | smberrclass = map->dos_class; |
| Possibly guarded | no |
Dismissed: map->dos_class comes from the kernel's own static ntstatus_to_dos_map[] table, not from the server's network packet. The server supplied only the lookup key (NT status code); the table entry contents are kernel-defined compile-time constants. Taint propagation through the lookup function's return value is a false positive. The dos_class field in the static table is defined to fit in a u8.
Finding #2 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le32_to_cpu() line 130 |
| Taint snippet | smberrcode = map->dos_code; |
| Tainted var | smberrcode |
| Truncation | line 130: 32 → 16-bit u16 |
| Sink snippet | smberrcode = map->dos_code; |
| Possibly guarded | no |
Dismissed: map->dos_code comes from the kernel's own static ntstatus_to_dos_map[] table, not from the server's network packet. Same reasoning as finding #1: the server controls only the lookup key, not the table entry values. The dos_code field is a kernel-defined constant intended to fit in a u16. False positive.
is_valid_oplock_break() — fs/smb/client/smb1misc.c BUG confidence=high
The function attempts to validate data_offset before constructing pnotify, but the check at line 88-93 has an integer underflow vulnerability: if len < sizeof(struct file_notify_information), the subtraction wraps around (unsigned arithmetic), making the guard trivially pass for any data_offset. Additionally, the pointer base (&hdr.Protocol) is 4 bytes into the buffer, so even a correct len-based check would need to account for that offset.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 86 |
| Taint snippet | data_offset = le32_to_cpu(pSMBr->DataOffset); |
| Tainted var | pnotify |
| Pointer deref | pnotify->FileName line 97 |
| Sink snippet | pnotify->FileName, pnotify->Action); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in is_valid_oplock_break when accessing pnotify->FileName or pnotify->Action with a crafted DataOffset; possible kernel info disclosure or panic
Fix: Before the data_offset check, verify that len >= sizeof(struct file_notify_information) to prevent unsigned underflow. Then check data_offset <= len - sizeof(struct file_notify_information). Also account for the 4-byte offset of &hdr.Protocol from the buffer start: ensure data_offset + sizeof(*pnotify) <= total_read - offsetof(smb_hdr, Protocol). Use explicit size_t arithmetic with overflow checks.
CVE pattern: SMB response DataOffset integer underflow / OOB read pattern, similar to CVE-2020-0796 style offset validation bugs in SMB parsing
cifs_query_path_info() — fs/smb/client/smb1ops.c ERROR confidence=low
Finding #1 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 665 |
| Sink snippet | &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1], |
| Possibly guarded | no |
Finding #2 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 668 |
| Sink snippet | ea->next_entry_offset = cpu_to_le32(0); |
| Possibly guarded | no |
Finding #3 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->flags line 669 |
| Sink snippet | ea->flags = 0; |
| Possibly guarded | no |
Finding #4 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_name_length line 670 |
| Sink snippet | ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN; |
| Possibly guarded | no |
Finding #5 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_value_length line 671 |
| Sink snippet | ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_MODE_SIZE); |
| Possibly guarded | no |
Finding #6 — Category A — unanalyzed
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Sink | memcpy() line 672 (arg 0, role=pointer) |
| Sink snippet | memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1); |
| Possibly guarded | no |
Finding #7 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 672 |
| Sink snippet | memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1); |
| Possibly guarded | no |
Finding #8 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 701 |
| Sink snippet | next = le32_to_cpu(ea->next_entry_offset); |
| Possibly guarded | no |
Finding #9 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_value_length line 703 |
| Sink snippet | if (le16_to_cpu(ea->ea_value_length)) { |
| Possibly guarded | no |
Finding #10 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 704 |
| Sink snippet | ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) + |
| Possibly guarded | no |
Finding #11 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_name_length line 705 |
| Sink snippet | ea->ea_name_length + 1 + |
| Possibly guarded | no |
Finding #12 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_value_length line 706 |
| Sink snippet | le16_to_cpu(ea->ea_value_length), 4)); |
| Possibly guarded | no |
Finding #13 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 711 |
| Sink snippet | &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1], |
| Possibly guarded | no |
Finding #14 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 714 |
| Sink snippet | ea->next_entry_offset = cpu_to_le32(0); |
| Possibly guarded | no |
Finding #15 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->flags line 715 |
| Sink snippet | ea->flags = 0; |
| Possibly guarded | no |
Finding #16 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_name_length line 716 |
| Sink snippet | ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN; |
| Possibly guarded | no |
Finding #17 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_value_length line 717 |
| Sink snippet | ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE); |
| Possibly guarded | no |
Finding #18 — Category A — unanalyzed
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Sink | memcpy() line 718 (arg 0, role=pointer) |
| Sink snippet | memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1); |
| Possibly guarded | no |
Finding #19 — Category E — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 707 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 718 |
| Sink snippet | memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1); |
| Possibly guarded | no |
Finding #20 — Category E — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Pointer deref | ea-> line 6214 |
| Sink snippet | list_len = le32_to_cpu(ea_response_data->list_len); |
| Possibly guarded | no |
Finding #21 — Category E — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Pointer deref | ea-> line 6236 |
| Sink snippet | temp_fea = &ea_response_data->list; |
| Possibly guarded | yes (heuristic) |
Finding #22 — Category F — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Loop | while_loop line 6238 |
| Sink snippet | while (list_len > 0) { |
| Possibly guarded | yes (heuristic) |
Finding #23 — Category E — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Pointer deref | ea-> line 6251 |
| Sink snippet | name_len = temp_fea->name_len; |
| Possibly guarded | no |
Finding #24 — Category E — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Pointer deref | ea-> line 6252 |
| Sink snippet | value_len = le16_to_cpu(temp_fea->value_len); |
| Possibly guarded | no |
Finding #25 — Category B — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Sink (in callee) | memcmp() line 6262 (arg 2, role=size) |
| Sink snippet | memcmp(ea_name, temp_ptr, name_len) == 0) { |
| Possibly guarded | yes (heuristic) |
Finding #26 — Category B — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Sink (in callee) | memcpy() line 6271 (arg 2, role=size) |
| Sink snippet | memcpy(EAData, temp_ptr, value_len); |
| Possibly guarded | yes (heuristic) |
Finding #27 — Category B — cross-function via CIFSSMBQAllEAs() — unanalyzed
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 661 |
| Taint snippet | ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset)); |
| Tainted var | ea |
| Call site | line 664 — passes ea to CIFSSMBQAllEAs() |
| Call snippet | rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE, |
| Sink (in callee) | memcpy() line 6280 (arg 2, role=size) |
| Sink snippet | memcpy(EAData, temp_ptr, name_len); |
| Possibly guarded | yes (heuristic) |
coalesce_t2() — fs/smb/client/smb1transport.c BUG confidence=high
coalesce_t2() reads DataOffset and DataCount from server-supplied SMB T2 response buffers without validating that the resulting pointer arithmetic stays within the actual allocated buffer boundaries. The checks present (remaining < 0, total_in_tgt > USHRT_MAX, byte_count > CIFSMaxBufSize) do not constrain DataOffset values, so crafted server responses can cause out-of-bounds reads and writes via memcpy.
Finding #1 — Category B — BUG oob_read
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | get_unaligned_le16() line 370 |
| Taint snippet | total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); |
| Tainted var | total_in_src |
| Sink | memcpy() line 416 (arg 2, role=size) |
| Sink snippet | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds in coalesce_t2 reading from second_buf beyond its allocation; kernel panic or info disclosure
Fix: Validate that DataOffset + DataCount from the source buffer does not exceed the actual received PDU size of second_buf before computing data_area_of_src and calling memcpy. Also validate total_in_src against the destination remaining capacity independently of the BCC/smbCalcSize check.
CVE pattern: SMB DataOffset out-of-bounds read, similar in class to CVE-2011-2524 style SMB response parsing bugs
Finding #2 — Category A — BUG oob_write
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | get_unaligned_le16() line 375 |
| Taint snippet | data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + |
| Tainted var | data_area_of_tgt |
| Sink | memcpy() line 416 (arg 0, role=pointer) |
| Sink snippet | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds write in coalesce_t2; heap corruption leading to kernel panic or privilege escalation
Fix: Validate that DataOffset (from target) + DataCount (from target) does not exceed the target buffer's allocated size (CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) before computing data_area_of_tgt. The smbCalcSize check at line 408 fires after put_bcc modifies the header but does not directly validate DataOffset.
CVE pattern: SMB DataOffset OOB write in coalesce path
Finding #3 — Category A — BUG oob_read
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | get_unaligned_le16() line 379 |
| Taint snippet | data_area_of_src = (char *)&pSMBs->hdr.Protocol + |
| Tainted var | data_area_of_src |
| Sink | memcpy() line 416 (arg 1, role=pointer) |
| Sink snippet | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds read in coalesce_t2 when copying from source buffer; kernel crash or information disclosure
Fix: Validate that DataOffset from the source SMB header does not cause data_area_of_src to point outside the received second_buf. Specifically: check that DataOffset <= (received_pdu_len - DataCount) before using these fields to compute data_area_of_src.
CVE pattern: SMB T2 secondary response DataOffset OOB read
smb2_parse_symlink_response() — fs/smb/client/smb2file.c FP confidence=high
smb2_parse_symlink_response performs explicit bounds validation of sub_offs+sub_len and print_offs+print_len against the iov buffer before calling the callee. Inside smb2_parse_native_symlink(), the tainted buf/len parameters are consumed by cifs_strndup_from_utf16() (a bounded conversion), producing a kernel-allocated null-terminated smb_target string. All subsequent allocations (lines 939, 964) and memcpy operations (lines 944, 946, 974) are sized using strlen() of kernel-derived strings, not directly from sub_offs or sub_len. The taint tracer incorrectly propagated taint through strlen() results. No viable counterexample exists where the callee's memory operations could overflow given the upstream bounds check and the strlen-based sizing.
Finding #1 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 145 |
| Taint snippet | sub_offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | sub_offs |
| Call site | line 155 — passes sub_offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | kmalloc() line 939 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(symlinkroot_len + 1 + abs_path_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: sub_offs is validated at lines 149-153 to fit within iov. In the callee, abs_path_len = strlen(abs_path)+1 is derived from a kernel-allocated string, not from sub_offs. kmalloc at line 939 is sized by strlen results. No counterexample possible.
Finding #2 — Category A — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 145 |
| Taint snippet | sub_offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | sub_offs |
| Call site | line 155 — passes sub_offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 944 (arg 0, role=pointer) |
| Sink snippet | memcpy(linux_target, symroot, symlinkroot_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 944 copies symroot (a kernel config string) into linux_target sized by strlen(symroot). Neither the destination nor size depends on sub_offs. False positive from taint propagation through callee argument.
Finding #3 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 145 |
| Taint snippet | sub_offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | sub_offs |
| Call site | line 155 — passes sub_offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 946 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + symlinkroot_len + 1, abs_path, abs_path_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 946 uses abs_path_len = strlen(abs_path)+1 as size, and linux_target was allocated to hold exactly symlinkroot_len+1+abs_path_len. Sizes are matched. No overflow possible.
Finding #4 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 145 |
| Taint snippet | sub_offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | sub_offs |
| Call site | line 155 — passes sub_offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | kmalloc() line 964 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: kmalloc at line 964 uses levels*3 + smb_target_len where levels comes from counting separators in full_path (kernel path) and smb_target_len = strlen(smb_target)+1 from a kernel-allocated string. No direct use of sub_offs here.
Finding #5 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 145 |
| Taint snippet | sub_offs = le16_to_cpu(sym->SubstituteNameOffset); |
| Tainted var | sub_offs |
| Call site | line 155 — passes sub_offs to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 974 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 974 uses smb_target_len = strlen(smb_target)+1 as size, and linux_target was allocated to hold levels*3 + smb_target_len. Allocation and copy sizes match. False positive.
Finding #6 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 144 |
| Taint snippet | sub_len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | sub_len |
| Call site | line 155 — passes sub_len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | kmalloc() line 939 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(symlinkroot_len + 1 + abs_path_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: sub_len is validated at lines 149-153. In the callee, sub_len bounds the UTF-16 strndup call; abs_path_len is then strlen of the result. kmalloc at 939 is sized by strlen values. No overflow possible.
Finding #7 — Category A — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 144 |
| Taint snippet | sub_len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | sub_len |
| Call site | line 155 — passes sub_len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 944 (arg 0, role=pointer) |
| Sink snippet | memcpy(linux_target, symroot, symlinkroot_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 944 copies symroot into linux_target; neither depends on sub_len after the UTF-16 conversion. False positive from taint tracking through callee argument.
Finding #8 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 144 |
| Taint snippet | sub_len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | sub_len |
| Call site | line 155 — passes sub_len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 946 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + symlinkroot_len + 1, abs_path, abs_path_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 946 uses abs_path_len = strlen(abs_path)+1 which is sized from kernel string, and linux_target was allocated to match. No overflow.
Finding #9 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 144 |
| Taint snippet | sub_len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | sub_len |
| Call site | line 155 — passes sub_len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | kmalloc() line 964 (arg 0, role=size_mul_overflow) |
| Sink snippet | linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL); |
| Possibly guarded | yes (heuristic) |
Dismissed: kmalloc at line 964 uses levels*3 + smb_target_len from strlen of kernel strings. sub_len only influences this indirectly through the strndup-bounded UTF-16 conversion. The resulting allocation is safely sized.
Finding #10 — Category B — cross-function via smb2_parse_native_symlink() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 144 |
| Taint snippet | sub_len = le16_to_cpu(sym->SubstituteNameLength); |
| Tainted var | sub_len |
| Call site | line 155 — passes sub_len to smb2_parse_native_symlink() |
| Call snippet | return smb2_parse_native_symlink(path, |
| Sink (in callee) | memcpy() line 974 (arg 2, role=size) |
| Sink snippet | memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ |
| Possibly guarded | yes (heuristic) |
Dismissed: memcpy at line 974 copies smb_target_len bytes (from strlen) into linux_target allocated to hold exactly that. Sizes match. False positive from over-propagation of sub_len taint through strndup conversion.
check_wsl_eas() — fs/smb/client/smb2inode.c FP confidence=high
check_wsl_eas() IS the validation function. It performs careful bounds checks: (1) outlen range check at lines 117-119; (2) ea_end <= iov_end at lines 125-126; (3) sizeof(*ea) fits before ea_end at line 129 (protecting all struct field reads); (4) nlen is pinned to the compile-time constant SMB2_WSL_XATTR_NAME_LEN at line 134 before any strncmp call; (5) ea->ea_data + nlen + 1 + vlen <= ea_end at line 135. All tainted accesses occur after sufficient guards. The scanner flagged the validation logic itself as vulnerable. | check_wsl_eas() is a well-structured validator with tight per-iteration bounds checks. The loop guard at line 129 verifies sizeof(*ea) bytes are available before any field access in that iteration, including next_entry_offset at line 160. The initial outlen range check, ea_end vs iov_end check, and per-iteration guard collectively ensure all struct field accesses are within the received packet buffer. The scanner flagged the offset arithmetic used to initialize ea, but missed that the subsequent per-iteration sizeof(*ea) guard covers all fields including the flagged one.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le16_to_cpu() line 132 |
| Taint snippet | nlen = ea->ea_name_length; |
| Tainted var | nlen |
| Truncation | line 132: 16 → 8-bit u8 |
| Sink snippet | nlen = ea->ea_name_length; |
| Possibly guarded | yes (heuristic) |
Dismissed: ea_name_length is a __u8 field in smb2_file_full_ea_info, not a 16-bit field. No truncation occurs. The taint propagation from le16_to_cpu() is through the ea pointer derivation, not through the field type. Additionally, nlen is validated to equal SMB2_WSL_XATTR_NAME_LEN at line 134 before any use. False positive.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_name_length line 132 |
| Sink snippet | nlen = ea->ea_name_length; |
| Possibly guarded | yes (heuristic) |
Dismissed: Line 129 checks (u8*)ea > ea_end - sizeof(*ea) before any field access, ensuring the full struct fits within the validated buffer region. Cannot construct counterexample: if ea + sizeof(*ea) > ea_end the function returns -EINVAL before the read.
Finding #3 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_value_length line 133 |
| Sink snippet | vlen = le16_to_cpu(ea->ea_value_length); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as finding #2 — sizeof(*ea) check at line 129 covers ea_value_length field. False positive.
Finding #4 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 135 |
| Sink snippet | (u8 *)ea->ea_data + nlen + 1 + vlen > ea_end) |
| Possibly guarded | yes (heuristic) |
Dismissed: ea_data is a flexible array member at the end of the struct; the sizeof(*ea) check at line 129 ensures at least the fixed part fits. The access at line 135 is itself a bounds check computing whether ea_data + nlen + 1 + vlen exceeds ea_end. This is validation logic, not a vulnerable sink.
Finding #5 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 140 (arg 2, role=size) |
| Sink snippet | if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) && |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen is checked at line 134: if nlen != SMB2_WSL_XATTR_NAME_LEN return -EINVAL. So at line 140, nlen equals the compile-time constant SMB2_WSL_XATTR_NAME_LEN. No counterexample possible — nlen is pinned to a constant value before strncmp.
Finding #6 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 140 |
| Sink snippet | if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) && |
| Possibly guarded | yes (heuristic) |
Dismissed: sizeof(*ea) validated at line 129; ea_data bounds validated at line 135 before strncmp calls at line 140. False positive.
Finding #7 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 141 (arg 2, role=size) |
| Sink snippet | strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) && |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as finding #5 — nlen pinned to SMB2_WSL_XATTR_NAME_LEN by line 134 check. False positive.
Finding #8 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 141 |
| Sink snippet | strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) && |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as finding #6 — sizeof and data bounds validated before access. False positive.
Finding #9 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 142 (arg 2, role=size) |
| Sink snippet | strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #10 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 142 |
| Sink snippet | strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #11 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 146 (arg 2, role=size) |
| Sink snippet | if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #12 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 146 |
| Sink snippet | if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #13 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 150 (arg 2, role=size) |
| Sink snippet | if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #14 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 150 |
| Sink snippet | if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #15 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 151 (arg 2, role=size) |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #16 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 151 |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #17 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 152 (arg 2, role=size) |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #18 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 152 |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) || |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #19 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | nlen |
| Sink | strncmp() line 153 (arg 2, role=size) |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: nlen pinned to compile-time constant by line 134. False positive.
Finding #20 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->ea_data line 153 |
| Sink snippet | !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) |
| Possibly guarded | yes (heuristic) |
Dismissed: Bounds validated before access. False positive.
Finding #21 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 121 |
| Taint snippet | ea = (void *)((u8 *)rsp_iov->iov_base + |
| Tainted var | ea |
| Pointer deref | ea->next_entry_offset line 160 |
| Sink snippet | next = le32_to_cpu(ea->next_entry_offset); |
| Possibly guarded | yes (heuristic) |
Dismissed: Line 129 checks `(u8 *)ea > ea_end - sizeof(*ea)`, i.e. ensures at least sizeof(*ea) bytes remain before ea_end on every loop iteration. Since next_entry_offset is a field within struct smb2_file_full_ea_info, this guard fully covers the dereference at line 160. No counterexample can pass line 129 while causing OOB at line 160 — the guard is structurally tight. False positive.
parse_posix_sids() — fs/smb/client/smb2inode.c FP confidence=high
The function has a complete validation chain. Before parse_posix_sids() is called, smb2_validate_and_copy_iov() verifies OutputBufferOffset and OutputBufferLength against the actual iov buffer size. Inside parse_posix_sids(), posix_info_sid_size() validates: (1) the SID fits within [sidsbuf, sidsbuf_end], (2) the subauth count is in [1,15], making the maximum return value 68 bytes (1+1+6+4*15). The destination structs (posix_owner, posix_group of type cifs_sid) are sized to accommodate a maximum SID. No counterexample can be constructed that passes all guards yet causes OOB access.
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 75 |
| Taint snippet | sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; |
| Tainted var | owner_len |
| Sink | memcpy() line 82 (arg 2, role=size) |
| Sink snippet | memcpy(&data->posix_owner, sidsbuf, owner_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: owner_len is the return value of posix_info_sid_size(), which (a) validates that sidsbuf+owner_len <= sidsbuf_end (no OOB read of source), and (b) caps owner_len at max 68 (subauth in [1,15]). The destination data->posix_owner is struct cifs_sid which is sized to hold a maximum SID of 68 bytes. Cannot construct a counterexample.
Finding #2 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 75 |
| Taint snippet | sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; |
| Tainted var | sidsbuf |
| Sink | memcpy() line 82 (arg 1, role=pointer) |
| Sink snippet | memcpy(&data->posix_owner, sidsbuf, owner_len); |
| Possibly guarded | no |
Dismissed: sidsbuf is derived from server-supplied OutputBufferOffset, but smb2_validate_and_copy_iov() (called before parse_posix_sids) validates OutputBufferOffset+length fits within the iov. Additionally, posix_info_sid_size() checks beg+total <= end before returning, confirming sidsbuf and its contents are within bounds.
Finding #3 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 75 |
| Taint snippet | sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; |
| Tainted var | group_len |
| Sink | memcpy() line 87 (arg 2, role=size) |
| Sink snippet | memcpy(&data->posix_group, sidsbuf + owner_len, group_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: group_len is returned by posix_info_sid_size(sidsbuf+owner_len, sidsbuf_end), which validates the group SID fits within [sidsbuf+owner_len, sidsbuf_end]. group_len is bounded to max 68 bytes by the subauth in [1,15] check. Cannot construct a counterexample.
Finding #4 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 75 |
| Taint snippet | sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; |
| Tainted var | sidsbuf |
| Sink | memcpy() line 87 (arg 1, role=pointer) |
| Sink snippet | memcpy(&data->posix_group, sidsbuf + owner_len, group_len); |
| Possibly guarded | no |
Dismissed: sidsbuf+owner_len is the base pointer passed to posix_info_sid_size() for the group SID, which verifies beg+2 <= end before reading, and beg+total <= end before returning. The pointer arithmetic is validated by posix_info_sid_size() before memcpy uses it.
reparse_buf_ptr() — fs/smb/client/smb2inode.c BUG confidence=high
The function validates off+count fits in the IOV buffer, then dereferences buf->ReparseDataLength before checking that count >= sizeof(*buf). The structural size check (count < len) happens after the dereference on line 43, not before it, allowing a server to supply a small OutputCount value that passes the overflow check but causes an OOB read when the struct fields are accessed.
Finding #1 — Category E — BUG oob_read
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 36 |
| Taint snippet | off = le32_to_cpu(io->OutputOffset); |
| Tainted var | buf |
| Pointer deref | buf->ReparseDataLength line 43 |
| Sink snippet | rdlen = le16_to_cpu(buf->ReparseDataLength); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in reparse_buf_ptr reading buf->ReparseDataLength; kernel crash or information disclosure if a server returns OutputOffset near the end of the buffer with OutputCount < sizeof(struct reparse_data_buffer)
Fix: Move the 'count < sizeof(*buf)' check to before the struct dereference: after computing buf on line 41, immediately check 'if (count < sizeof(*buf)) return ERR_PTR(-EIO);' before accessing buf->ReparseDataLength on line 43. The current check at line 45 needs to precede line 43.
CVE pattern: DataOffset OOB read — server-controlled offset and length allow struct field access beyond validated buffer region
smb2_compound_op() — fs/smb/client/smb2inode.c FP confidence=high
The flagged call to smb2_validate_and_copy_iov() is itself the validation function. The callee source clearly shows that smb2_validate_and_copy_iov() calls smb2_validate_iov() on the offset and buffer_length before computing begin_of_buf and performing the memcpy. The memcpy at line 3868 is only reached after smb2_validate_iov() returns 0, meaning the offset+buffer_length combination has already been validated to fit within the iov buffer. This is a validator-internal access pattern (false positive category 2/4 in the guidelines): the flagged sink IS the validation logic, not a vulnerable sink.
Finding #1 — Category A — cross-function via smb2_validate_and_copy_iov() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 608 |
| Taint snippet | rc = smb2_validate_and_copy_iov( |
| Tainted var | le16_to_cpu(qi_rsp->OutputBufferOffset) |
| Call site | line 608 — passes le16_to_cpu(qi_rsp->OutputBufferOffset) to smb2_validate_and_copy_iov() |
| Call snippet | rc = smb2_validate_and_copy_iov( |
| Sink (in callee) | memcpy() line 3868 (arg 1, role=pointer) |
| Sink snippet | memcpy(data, begin_of_buf, minbufsize); |
| Possibly guarded | no |
Dismissed: qi_rsp is a server response buffer, so OutputBufferOffset is genuinely server-supplied and tainted. However, smb2_validate_and_copy_iov() is explicitly a validation-and-copy helper: it calls smb2_validate_iov(offset, buffer_length, iov, minbufsize) before computing begin_of_buf = offset + (char *)iov->iov_base and calling memcpy(). If smb2_validate_iov() succeeds, it has confirmed that offset + buffer_length fits within iov->iov_len and that buffer_length >= minbufsize. Therefore begin_of_buf is within the iov buffer and the memcpy of minbufsize bytes is safe. No counterexample can be constructed: any offset value that would cause OOB access would be rejected by smb2_validate_iov(). The scanner flagged the memcpy inside the callee as a sink without recognizing that the callee is a purpose-built validator that guards that very memcpy.
__smb2_calc_size() — fs/smb/client/smb2misc.c VALIDATE confidence=medium
The primary call site smb2_check_message() likely validates shdr->Command in the 19 omitted lines (since it also uses 'command' as array subscript at line 218 with smb2_rsp_struct_sizes). However, the secondary call site smb2_calc_size() performs no validation before calling __smb2_calc_size(), leaving a potential OOB array read if Command is out of range. The finding is plausible for the smb2_calc_size() path but likely a false positive for the smb2_check_message() path.
Finding #1 — Category C — BUG oob_read
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 454 |
| Taint snippet | if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) |
| Tainted var | le16_to_cpu(shdr->Command) |
| Subscript | [] line 454 |
| Sink snippet | if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: global-out-of-bounds in __smb2_calc_size when server sends malformed Command value; potential info disclosure or kernel panic
Fix: Before indexing has_smb2_data_area[], validate that le16_to_cpu(shdr->Command) is less than the array size (e.g., SMB2_NUM_CMDS or ARRAY_SIZE(has_smb2_data_area)). Add: 'if (command >= ARRAY_SIZE(has_smb2_data_area)) goto calc_size_exit;' or validate command in smb2_calc_size() before calling __smb2_calc_size().
CVE pattern: Out-of-bounds array read via unvalidated server-supplied command field in SMB2 message parsing
smb2_check_message() — fs/smb/client/smb2misc.c FP confidence=high
The function smb2_check_message() is a validation function for server responses. The 'command' value is indeed server-supplied (read from a received network buffer via le16_to_cpu(shdr->Command)). However, the bounds check at lines 193-196 explicitly validates 'command' against NUMBER_OF_SMB2_COMMANDS before the array subscript at line 218. The check 'if (command >= NUMBER_OF_SMB2_COMMANDS)' with an early return of 1 is a proper and sufficient bounds guard. The array smb2_rsp_struct_sizes is indexed by command values 0..NUMBER_OF_SMB2_COMMANDS-1, and any out-of-range value causes an early return before the subscript.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 192 |
| Taint snippet | command = le16_to_cpu(shdr->Command); |
| Tainted var | command |
| Subscript | [] line 218 |
| Sink snippet | if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { |
| Possibly guarded | yes (heuristic) |
Dismissed: The guard at lines 193-196 checks 'if (command >= NUMBER_OF_SMB2_COMMANDS) { return 1; }' which is a tight upper-bound check. After this check, command is in [0, NUMBER_OF_SMB2_COMMANDS-1], which is exactly the valid index range for smb2_rsp_struct_sizes[]. No counterexample can be constructed: any value of command that would index out-of-bounds (>= NUMBER_OF_SMB2_COMMANDS) is rejected before reaching line 218. The scanner's 'possibly guarded' flag correctly detected the conditional but did not confirm its sufficiency — in this case it is fully sufficient.
smb2_tcon_find_pending_open_lease() — fs/smb/client/smb2misc.c VALIDATE confidence=medium
The rsp pointer is a cast from a network buffer (server response), so rsp->NewLeaseState is genuinely server-supplied. The value is a 32-bit lease state field that is silently truncated to __u8 when stored in 'lease_state'. The lease_state value is later assigned to open->oplock. The question is whether valid SMB2 lease states fit within 8 bits. SMB2 lease states are defined as: SMB2_LEASE_NONE=0x00, SMB2_LEASE_READ=0x01, SMB2_LEASE_HANDLE=0x02, SMB2_LEASE_WRITE=0x04, and combinations thereof (max 0x07). So in practice, a compliant server would only ever set values 0-7. However, a malicious server could set high bits (e.g., 0x100) that would be silently dropped, causing the oplock to appear as a different (lower) state than intended. This could lead to incorrect oplock/lease state tracking, but not memory corruption.
Finding #1 — Category H — BUG integer_overflow
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le32_to_cpu() line 612 |
| Taint snippet | __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); |
| Tainted var | lease_state |
| Truncation | line 612: 32 → 8-bit __u8 |
| Sink snippet | __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: Incorrect lease/oplock state stored in open->oplock; a malicious server sending NewLeaseState=0x100 would result in oplock=0x00 (NONE) instead of a non-zero value, potentially causing the client to believe it has no lease when it does or vice versa, leading to cache coherency bugs or missed lease break acknowledgements.
Fix: Validate that rsp->NewLeaseState contains only known lease state bits before truncating: u32 raw_state = le32_to_cpu(rsp->NewLeaseState); if (raw_state & ~(SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE)) { /* log and reject or mask */ } __u8 lease_state = raw_state & 0x07; Alternatively, change the type of open->oplock and lease_state to u32 to avoid truncation.
smb2_tcon_has_lease() — fs/smb/client/smb2misc.c BUG confidence=medium
The function reads rsp->NewLeaseState from a server-supplied network buffer via le32_to_cpu(), producing a 32-bit value, then silently truncates it to an 8-bit __u8. The SMB2 lease state field is a 32-bit bitmask (e.g., SMB2_LEASE_READ_CACHING=0x01, WRITE=0x02, HANDLE=0x04), so legitimate values fit in 8 bits in practice — but a malicious server could supply a value with bits set above bit 7, which would be silently dropped. The truncated value is later stored in cfile->oplock_level and used in oplock break processing logic. No masking or explicit bounds check is applied before the assignment.
Finding #1 — Category H — BUG integer_overflow
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le32_to_cpu() line 578 |
| Taint snippet | lease_state = le32_to_cpu(rsp->NewLeaseState); |
| Tainted var | lease_state |
| Truncation | line 578: 32 → 8-bit u8 |
| Sink snippet | lease_state = le32_to_cpu(rsp->NewLeaseState); |
| Possibly guarded | no |
| Server-supplied | yes |
| Check present | no |
| Check sufficient | no |
Symptom: A malicious server could send a lease break with NewLeaseState having bits set above bit 7; the upper bits would be silently truncated, causing the kernel to record an incorrect lease/oplock level in cfile->oplock_level. This could lead to incorrect caching decisions or failure to properly invalidate page cache, potentially resulting in data corruption or security policy bypass rather than a crash.
Fix: Either declare lease_state as __le32/__u32 (matching the protocol field width) and update all consumers to handle 32-bit values, or explicitly mask the value before truncation: lease_state = le32_to_cpu(rsp->NewLeaseState) & 0xFF; and add a check that the high bits are zero (returning false or logging a warning if a server sends an unexpected value). The cleanest fix is to widen cfile->oplock_level to u32 or validate that NewLeaseState only contains known bits (SMB2_LEASE_READ_CACHING | SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_HANDLE_CACHING).
CVE pattern: Silent integer truncation of server-supplied field before use in state machine logic
crypt_message() — fs/smb/client/smb2ops.c FP confidence=high
The scanner misidentified the taint flow. le64_to_cpu(tr_hdr->SessionId) at line 4627 is passed as a u64 argument to smb2_get_enc_key(), and the RETURN VALUE of smb2_get_enc_key() is stored in 'rc' (an int). The 64-bit value is not truncated into 'rc'; 'rc' receives the integer error code returned by the function call. The le64_to_cpu() result is passed by value to the function and is never assigned to 'rc'. This is a classic taint-tracking false positive where the scanner incorrectly propagated taint from a function argument through the function's return value.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le64_to_cpu() line 4627 |
| Taint snippet | rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); |
| Tainted var | rc |
| Truncation | line 4627: 64 → 32-bit u32 |
| Sink snippet | rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); |
| Possibly guarded | no |
Dismissed: The scanner claims le64_to_cpu(tr_hdr->SessionId) is truncated into 'rc' (32-bit), but this is incorrect. The expression 'rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key)' stores the RETURN VALUE of smb2_get_enc_key() into 'rc', not the 64-bit SessionId argument. The SessionId (u64) is passed by value as the second argument to smb2_get_enc_key() and is not truncated — it is used as a lookup key inside that function. 'rc' receives an int error code from the callee. No truncation of a 64-bit server-supplied value into 'rc' occurs. This is a false positive due to overly conservative taint propagation through function call arguments.
move_smb2_ea_to_cifs() — fs/smb/client/smb2ops.c FP confidence=high
The function move_smb2_ea_to_cifs() has solid validation discipline. For finding #1, value_len is bounded by the src_size check at line 1064 (source buffer) and by explicit dst_size checks before memcpy (destination buffer). For finding #2, the new src pointer is only dereferenced to read next_entry_offset at line 1113 in the NEXT iteration, and before that dereference the loop checks src_size > 0, plus the src_size >= next_entry_offset check at line 1113 ensures at least that many bytes remain. Since smb2_file_full_ea_info is small and the remaining src_size is validated against next_entry_offset before the advance, the new pointer is within bounds.
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1059 |
| Taint snippet | value_len = (size_t)le16_to_cpu(src->ea_value_length); |
| Tainted var | value_len |
| Sink | memcpy() line 1084 (arg 2, role=size) |
| Sink snippet | memcpy(dst, value, value_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: For source buffer: line 1064 checks src_size < 8 + name_len + 1 + value_len, ensuring value_len bytes are present in the source after the header and name. For destination buffer: line 1080 checks dst_size < value_len and returns -ERANGE if true; only if dst_size >= value_len does execution reach memcpy(dst, value, value_len) at line 1084. No counterexample can be constructed — any value_len that passes both guards is safe for both source and destination.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1119 |
| Taint snippet | src = (void *)((char *)src + |
| Tainted var | src |
| Pointer deref | src->next_entry_offset line 1120 |
| Sink snippet | le32_to_cpu(src->next_entry_offset)); |
| Possibly guarded | no |
Dismissed: The pointer advance at lines 1119-1120 is preceded by two guards: (1) line 1110 breaks if next_entry_offset is 0, avoiding infinite loops; (2) line 1113 checks src_size < le32_to_cpu(src->next_entry_offset) and breaks with -ERANGE if true. After the advance, src_size is decremented by next_entry_offset at line 1118. The new src pointer thus points within the original buffer (the advance is <= src_size bytes from the current position). On the next iteration, src_size > 0 is checked and any subsequent struct field read is within the validated remaining buffer. The deref of src->next_entry_offset at line 1120 (the le32_to_cpu call completing the assignment) is at the same address as line 1113's read, so it is within bounds by the guard already evaluated.
parse_server_interfaces() — fs/smb/client/smb2ops.c FP confidence=high
parse_server_interfaces() has reasonable validation discipline: it checks `next > bytes_left` before advancing `p`, and uses short-circuit evaluation to guard the post-loop `p->Next` access with a bytes_left size check. The scanner flagged the post-loop p->Next access at line 807, but the `&&` short-circuit on line 806 (bytes_left >= offsetof(Next)+sizeof(p->Next)) ensures the access is within the remaining buffer before dereferencing p->Next.
Finding #1 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 782 |
| Taint snippet | next = le32_to_cpu(p->Next); |
| Tainted var | p |
| Pointer deref | p->Next line 807 |
| Sink snippet | + sizeof(p->Next) && p->Next)) |
| Possibly guarded | no |
Dismissed: The access `p->Next` at line 807 is protected by the short-circuit `&&` condition on line 806: `bytes_left >= offsetof(struct network_interface_info_ioctl_rsp, Next) + sizeof(p->Next)` must evaluate to true before `p->Next` is read. Since `bytes_left` is decremented by validated `next` values (each checked against `bytes_left` at line 788), `p + bytes_left` always stays within the original buffer. No counterexample can be constructed: any `bytes_left` small enough to allow OOB would fail the offsetof check and short-circuit the &&, preventing the dereference.
receive_encrypted_standard() — fs/smb/client/smb2ops.c BUG confidence=medium
The function reads next_cmd from a decrypted server response and uses it in a memcpy. While there is a guard preventing next_cmd > pdu_length (preventing source OOB read and size underflow), there is no check ensuring the destination buffer (cifs_buf_get or cifs_small_buf_get, whose sizes are fixed pool allocations) is large enough to hold pdu_length - next_cmd bytes. The next_is_large flag controlling buffer type selection is not re-evaluated against the actual remaining data size.
Finding #1 — Category B — BUG oob_write
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 5252 |
| Taint snippet | next_cmd = le32_to_cpu(shdr->NextCommand); |
| Tainted var | next_cmd |
| Sink | memcpy() line 5270 (arg 2, role=size) |
| Sink snippet | memcpy(next_buffer, buf + next_cmd, pdu_length - next_cmd); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: BUG: KASAN: slab-out-of-bounds in receive_encrypted_standard when copying compound PDU remainder into undersized buffer; heap corruption potentially leading to kernel panic or privilege escalation
Fix: Before selecting next_buffer type (large vs small), compute the remaining data size (pdu_length - next_cmd) and ensure next_is_large is set to true if that size exceeds MAX_CIFS_SMALL_BUFFER_SIZE. Add a bounds check: if (pdu_length - next_cmd > (next_is_large ? CIFSMaxBufSize + MAX_HEADER_SIZE(server) : MAX_CIFS_SMALL_BUFFER_SIZE)) return -1; Additionally verify next_cmd is at a valid aligned offset (e.g., >= sizeof(struct smb2_hdr)) before using it.
CVE pattern: SMB compound response heap overflow via malformed NextCommand offset
smb2_query_eas() — fs/smb/client/smb2ops.c FP confidence=high
smb2_query_eas() calls smb2_validate_iov() before using any server-supplied offsets or lengths, establishing that the info pointer and OutputBufferLength are within the received buffer. move_smb2_ea_to_cifs() then performs per-entry bounds checking at line 1064 (src_size < 8 + name_len + 1 + value_len) before any subscript or memcpy operations, and checks dst_size before writing. All flagged sinks are protected by this two-level validation chain.
Finding #1 — Category C — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 1170 |
| Taint snippet | info = (struct smb2_file_full_ea_info *)( |
| Tainted var | info |
| Call site | line 1172 — passes info to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Subscript (in callee) | [] line 1072 |
| Sink snippet | value = &src->ea_data[src->ea_name_length + 1]; |
| Possibly guarded | no |
Dismissed: smb2_validate_iov() validates the offset and OutputBufferLength fit in the iov buffer before info is computed. In move_smb2_ea_to_cifs(), the check at line 1064 ensures src_size >= 8 + name_len + 1 + value_len before accessing ea_data[name_len+1]. No counterexample: any name_len or value_len that would push the subscript out of bounds would fail the line 1064 check first. False positive.
Finding #2 — Category B — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1170 |
| Taint snippet | info = (struct smb2_file_full_ea_info *)( |
| Tainted var | info |
| Call site | line 1172 — passes info to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Sink (in callee) | memcmp() line 1076 (arg 2, role=size) |
| Sink snippet | memcmp(ea_name, name, name_len) == 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: name_len is derived from src->ea_name_length (1-byte field, max 255). The check at line 1064 ensures name_len fits within src_size before memcmp is called. Additionally, ea_name_len is from strlen(ea_name) which is caller-controlled (not server-supplied). The memcmp size is min(ea_name_len, name_len) effectively via the ea_name_len == name_len equality check first. False positive.
Finding #3 — Category B — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1170 |
| Taint snippet | info = (struct smb2_file_full_ea_info *)( |
| Tainted var | info |
| Call site | line 1172 — passes info to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Sink (in callee) | memcpy() line 1084 (arg 2, role=size) |
| Sink snippet | memcpy(dst, value, value_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: value_len is le16_to_cpu(src->ea_value_length), max 65535. Line 1064 checks src_size >= 8 + name_len + 1 + value_len, bounding value_len within the validated buffer. Line 1080 checks dst_size >= value_len before memcpy. Cannot construct counterexample: both buffer-side and destination-side checks are in place. False positive.
Finding #4 — Category B — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 1170 |
| Taint snippet | info = (struct smb2_file_full_ea_info *)( |
| Tainted var | info |
| Call site | line 1172 — passes info to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Sink (in callee) | memcpy() line 1098 (arg 2, role=size) |
| Sink snippet | memcpy(dst, src->ea_data, name_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: name_len (1-byte field) is checked at line 1064 to be within src_size. Line 1094 checks dst_size >= user_name_len (which includes name_len) before memcpy at line 1098. Both source and destination bounds are validated. False positive.
Finding #5 — Category E — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 1170 |
| Taint snippet | info = (struct smb2_file_full_ea_info *)( |
| Tainted var | info |
| Call site | line 1172 — passes info to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Pointer deref | info-> line 1120 |
| Sink snippet | le32_to_cpu(src->next_entry_offset)); |
| Possibly guarded | no |
Dismissed: Line 1120 accesses src->next_entry_offset for loop advancement. The src pointer at this point is within the validated iov buffer (smb2_validate_iov ensures OutputBufferLength fits). The struct field access itself (reading next_entry_offset from within the validated buffer) is safe as long as src points within the buffer. The callee code (not fully shown) likely validates next_entry_offset before advancing src, following the same pattern as smb2_parse_lease_buf and similar SMB2 parsers. The taint tracker conflates pointer-derivation taint with actual vulnerability. False positive.
Finding #6 — Category F — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 1172 |
| Taint snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Tainted var | le32_to_cpu(rsp->OutputBufferLength) |
| Call site | line 1172 — passes le32_to_cpu(rsp->OutputBufferLength) to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Loop | while_loop line 1057 |
| Sink snippet | while (src_size > 0) { |
| Possibly guarded | no |
Dismissed: OutputBufferLength controls the while loop bound as src_size. However, smb2_validate_iov() at line 1163 validates that OutputBufferOffset + OutputBufferLength fits within the iov buffer, so src_size is bounded by the actual received data size. The loop subtracts from src_size each iteration (via next_entry_offset advancement), and the per-entry check at line 1064 ensures each entry fits within remaining src_size. The loop is bounded by actual buffer content. False positive.
Finding #7 — Category B — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 1172 |
| Taint snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Tainted var | le32_to_cpu(rsp->OutputBufferLength) |
| Call site | line 1172 — passes le32_to_cpu(rsp->OutputBufferLength) to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Sink (in callee) | memcpy() line 1084 (arg 2, role=size) |
| Sink snippet | memcpy(dst, value, value_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same as finding #3 — value_len is doubly bounded: by the src_size check at line 1064 and by dst_size check at line 1080. The src_size itself is validated by smb2_validate_iov(). False positive.
Finding #8 — Category E — cross-function via move_smb2_ea_to_cifs() — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 1172 |
| Taint snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Tainted var | le32_to_cpu(rsp->OutputBufferLength) |
| Call site | line 1172 — passes le32_to_cpu(rsp->OutputBufferLength) to move_smb2_ea_to_cifs() |
| Call snippet | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
| Pointer deref | le32_to_cpu(rsp->OutputBufferLength)-> line 1120 |
| Sink snippet | le32_to_cpu(src->next_entry_offset)); |
| Possibly guarded | no |
Dismissed: Same as finding #5 — the src pointer is within the validated iov buffer (OutputBufferLength validated by smb2_validate_iov). The struct field access src->next_entry_offset reads from within the validated region. The taint on OutputBufferLength does not create a vulnerability at the point of reading next_entry_offset from the validated buffer. False positive.
smb3_enum_snapshots() — fs/smb/client/smb2ops.c FP confidence=high
ret_data_len is bounded by two guards before copy_to_user: (1) the actual server response length from SMB2_ioctl (bounded by max_response_size = size of retbuf allocation), and (2) a cap to snapshot_in.snapshot_array_size + sizeof(struct smb_snapshot_array), which is derived from the user's own declared buffer size. No counterexample can be constructed where ret_data_len exceeds either retbuf or the user-declared buffer.
Finding #1 — Category G2 — false positive
| Category | Cat G2 — unvalidated size argument to copy_from/to_user |
|---|---|
| Taint source | copy_to_user() line 2390 |
| Taint snippet | if (copy_to_user(ioc_buf, retbuf, ret_data_len)) |
| Tainted var | ret_data_len |
| Unvalidated size | copy_to_user() arg 2 line 2390 — size ret_data_len |
| Sink snippet | if (copy_to_user(ioc_buf, retbuf, ret_data_len)) |
| Possibly guarded | no |
Dismissed: ret_data_len after SMB2_ioctl() reflects actual received bytes, capped by max_response_size (the allocation size of retbuf). Before copy_to_user, it is further capped to snapshot_in.snapshot_array_size + sizeof(struct smb_snapshot_array), which is what the user declared as their buffer capacity. No counterexample found: the guards are sufficient on both the source buffer (retbuf) and destination buffer (ioc_buf) sides.
smb3_fiemap() — fs/smb/client/smb2ops.c FP confidence=high
The function has reasonable validation of out_data_len before array access. The flagged finding is a scanner error: rc receives the int return value of fiemap_fill_next_extent(), not a truncated le64_to_cpu() value. The le64_to_cpu() results are passed as u64 arguments to fiemap_fill_next_extent(), which accepts u64 parameters — no truncation occurs. A separate potential issue exists if num==0 and last_blob==0 (OOB at out_data[num-1]), but that is not what the scanner flagged.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le64_to_cpu() line 4170 |
| Taint snippet | rc = fiemap_fill_next_extent(fei, |
| Tainted var | rc |
| Truncation | line 4170: 64 → 32-bit u32 |
| Sink snippet | rc = fiemap_fill_next_extent(fei, |
| Possibly guarded | no |
Dismissed: The scanner incorrectly identifies rc as receiving a truncated 64-bit server-supplied value. In reality, rc is assigned the int return value of fiemap_fill_next_extent(). The le64_to_cpu() expressions produce u64 arguments passed to that function's u64 parameters — no truncation of server data into rc occurs. This is a scanner false positive from misattributing taint from the arguments to the return value of the same call expression.
smb3_simple_fallocate_range() — fs/smb/client/smb2ops.c FP confidence=high
The function has reasonable validation discipline: out_data_len is checked against sizeof(struct file_allocated_range_buffer) before accessing tmp_data fields, and range_start+range_len overflow is checked with check_add_overflow before use. The scanner's finding is based on a misidentification of the taint flow.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le64_to_cpu() line 3685 |
| Taint snippet | rc = smb3_simple_fallocate_write_range(xid, tcon, |
| Tainted var | rc |
| Truncation | line 3685: 64 → 32-bit u32 |
| Sink snippet | rc = smb3_simple_fallocate_write_range(xid, tcon, |
| Possibly guarded | no |
Dismissed: The scanner claims a 64-bit server value is truncated into 32-bit 'rc' at line 3685. However, 'rc' is assigned the return value of smb3_simple_fallocate_write_range(), which returns int — not a server-supplied 64-bit value. The server-supplied variable 'l' (loff_t, derived from range_start) is passed as an argument to that function, not assigned to rc. The taint propagation model is incorrect here. Furthermore, range_start and range_len from the server are protected by check_add_overflow() and the range_end > S64_MAX check before any use, and out_data_len is validated against sizeof(struct file_allocated_range_buffer) before tmp_data is dereferenced. No counterexample can be constructed because no truncation of server data into rc actually occurs.
SMB2_QFS_attr() — fs/smb/client/smb2pdu.c FP confidence=high
smb2_validate_iov() is called at line 6267 before any data access. It validates: (1) rsp_len >= min_len, (2) both values <= 0x7FFFFF, (3) [offset+iov_base, offset+iov_base+rsp_len] lies within the iov buffer. This establishes safety for all subsequent struct pointer dereferences and memcpy operations, since min_len equals sizeof() of each target struct for sector/volume levels, and the FS_ATTRIBUTE case uses min_t(unsigned int, rsp_len, min_len) to cap the destination write to sizeof(FILE_SYSTEM_ATTRIBUTE_INFO).
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 6265 |
| Taint snippet | rsp_len = le32_to_cpu(rsp->OutputBufferLength); |
| Tainted var | rsp_len |
| Sink | memcpy() line 6272 (arg 2, role=size) |
| Sink snippet | memcpy(&tcon->fsAttrInfo, offset |
| Possibly guarded | no |
Dismissed: smb2_validate_iov ensures rsp_len fits within the source iov (no OOB read). The destination is bounded by min_t(unsigned int, rsp_len, min_len) where min_len=sizeof(FILE_SYSTEM_ATTRIBUTE_INFO), so no OOB write. Cannot construct a counterexample: rsp_len passing smb2_validate_iov is <= iov_len-offset, and min_t caps destination write to min_len=struct size.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6266 |
| Taint snippet | offset = le16_to_cpu(rsp->OutputBufferOffset); |
| Tainted var | ss_info |
| Pointer deref | ss_info->Flags line 6281 |
| Sink snippet | tcon->ss_flags = le32_to_cpu(ss_info->Flags); |
| Possibly guarded | no |
Dismissed: smb2_validate_iov called with min_len=sizeof(struct smb3_fs_ss_info) ensures rsp_len>=sizeof(*ss_info) and offset+rsp_len fits in iov. Thus offset+sizeof(*ss_info)<=packet_end. The ss_info->Flags dereference is safe. No counterexample possible.
Finding #3 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6266 |
| Taint snippet | offset = le16_to_cpu(rsp->OutputBufferOffset); |
| Tainted var | ss_info |
| Pointer deref | ss_info->PhysicalBytesPerSectorForPerf line 6283 |
| Sink snippet | le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); |
| Possibly guarded | no |
Dismissed: Same guard as finding #2 protects ss_info->PhysicalBytesPerSectorForPerf. smb2_validate_iov guarantees the full struct fits in the packet buffer.
Finding #4 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6266 |
| Taint snippet | offset = le16_to_cpu(rsp->OutputBufferOffset); |
| Tainted var | vol_info |
| Pointer deref | vol_info->VolumeSerialNumber line 6287 |
| Sink snippet | tcon->vol_serial_number = le32_to_cpu(vol_info->VolumeSerialNumber); |
| Possibly guarded | no |
Dismissed: smb2_validate_iov called with min_len=sizeof(struct filesystem_vol_info) ensures rsp_len>=sizeof(*vol_info) and the region [offset, offset+rsp_len] fits in iov. vol_info->VolumeSerialNumber dereference is safe. No counterexample possible.
Finding #5 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le16_to_cpu() line 6266 |
| Taint snippet | offset = le16_to_cpu(rsp->OutputBufferOffset); |
| Tainted var | vol_info |
| Pointer deref | vol_info->VolumeCreationTime line 6288 |
| Sink snippet | tcon->vol_create_time = vol_info->VolumeCreationTime; |
| Possibly guarded | no |
Dismissed: Same guard as finding #4 protects vol_info->VolumeCreationTime. smb2_validate_iov guarantees the full filesystem_vol_info struct fits within the validated buffer region.
__smb2_plain_req_init() — fs/smb/client/smb2pdu.c FP confidence=high
The function __smb2_plain_req_init() is a request initializer — it builds outgoing SMB2 request packets. The smb2_command parameter is always a kernel-defined constant (SMB2_NEGOTIATE, SMB2_IOCTL, SMB2_SET_INFO, etc.) passed from callers, never a value parsed from a server response buffer. The le16_to_cpu() call is purely an endian normalization of an internally-controlled value, not a deserialization of server-supplied data. Both call sites confirm this: smb2_plain_req_init propagates a constant from its own callers, and smb2_ioctl_req_init passes SMB2_IOCTL literally.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 577 |
| Taint snippet | uint16_t com_code = le16_to_cpu(smb2_command); |
| Tainted var | com_code |
| Subscript | [] line 578 |
| Sink snippet | cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); |
| Possibly guarded | no |
Dismissed: The scanner mistakenly treats le16_to_cpu() as a server-supplied taint source, but smb2_command is a kernel-defined constant (e.g., SMB2_IOCTL=0x000B, SMB2_SET_INFO=0x0011) passed into this request-building function. No counterexample is possible because the values are compile-time constants controlled entirely by the kernel, not by any server response. This is a classic false positive from pattern 1: internally-constructed request fields round-tripped through endian conversion.
decode_compress_ctx() — fs/smb/client/smb2pdu.c FP confidence=high
The function decode_compress_ctx() performs careful multi-level validation before the loop. The count variable is bounded both by ARRAY_SIZE(ctxt->CompressionAlgorithms) (a compile-time constant limiting the array size) and by checking that len >= 8 + count * sizeof(__le16). These two checks together ensure that (1) count cannot exceed the statically-allocated array size in the struct, and (2) count * sizeof element fits within the declared DataLength. No counterexample can be constructed that passes both guards yet causes OOB access.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 853 |
| Taint snippet | count = le16_to_cpu(ctxt->CompressionAlgorithmCount); |
| Tainted var | count |
| Loop | for_loop line 866 |
| Sink snippet | for (i = 0; i < count; i++) { |
| Possibly guarded | yes (heuristic) |
Dismissed: At line 854-858, count is validated against two independent upper bounds: (a) count > ARRAY_SIZE(ctxt->CompressionAlgorithms) ensures the loop index i < count never exceeds the statically-declared array dimension, and (b) len < 8 + count * sizeof(__le16) ensures count * element_size fits within the DataLength field. A counterexample would require count > ARRAY_SIZE(ctxt->CompressionAlgorithms) or count * 2 > len - 8, both of which are caught by the guard that returns early. No counterexample exists — the checks are sufficient.
fill_small_buf() — fs/smb/client/smb2pdu.c FP confidence=high
The smb2_command parameter in fill_small_buf() is an internally-constructed kernel command code used when building outgoing SMB2 requests, not a value parsed from a server response buffer. The le16_to_cpu() call on this parameter triggers the taint analysis, but the value originates from kernel-internal logic in __smb2_plain_req_init() and its callers, not from network data. The array indexing into smb2_req_struct_sizes[] is therefore not a server-controlled OOB risk.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 534 |
| Taint snippet | __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; |
| Tainted var | le16_to_cpu(smb2_command) |
| Subscript | [] line 534 |
| Sink snippet | __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; |
| Possibly guarded | no |
Dismissed: smb2_command is a kernel-internal command code constant (e.g., SMB2_SET_INFO, SMB2_QUERY_INFO) passed from kernel request-building logic, not read from a server response. The le16_to_cpu() call on this value falsely triggers taint marking. No counterexample can be constructed because the value set is bounded by kernel-defined SMB2 command enumerations. This is a classic false positive from pattern #1: endian conversion on a locally-written field.
parse_posix_ctxt() — fs/smb/client/smb2pdu.c FP confidence=high
parse_posix_ctxt() is well-protected. The caller smb2_parse_contexts() validates that DataOffset+DataLength fits within the response iov before calling parse_posix_ctxt(), establishing safe beg/end pointers. Inside parse_posix_ctxt(), posix_info_sid_size() validates (a) at least 2 bytes readable, (b) subauth in [1,15], and (c) beg+total <= end before returning sid_len. This guarantees source-buffer safety. For destination safety, the maximum return value of posix_info_sid_size is 1+1+6+4*15=68 bytes, which exactly equals sizeof(struct cifs_sid) (the type of posix->owner and posix->group). No counterexample can pass all guards and still cause OOB read or write.
Finding #1 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 2393 |
| Taint snippet | u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); |
| Tainted var | sid_len |
| Sink | memcpy() line 2409 (arg 2, role=size) |
| Sink snippet | memcpy(&posix->owner, sid, sid_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: sid_len is the return value of posix_info_sid_size(), which (a) verifies sid+sid_len <= end (source buffer safety, where end is within the validated response iov), and (b) is structurally capped at 68 bytes (subauth<=15 => total=1+1+6+60=68), matching sizeof(struct cifs_sid). No counterexample exists: any value passing posix_info_sid_size's guards satisfies both source and destination bounds.
Finding #2 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 2393 |
| Taint snippet | u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); |
| Tainted var | sid |
| Sink | memcpy() line 2409 (arg 1, role=pointer) |
| Sink snippet | memcpy(&posix->owner, sid, sid_len); |
| Possibly guarded | no |
Dismissed: The pointer 'sid' is derived from beg+12 where beg=(u8*)cc+DataOffset. The caller validates DataOffset+DataLength<=rem (within iov). posix_info_sid_size(sid, end) checks sid+2<=end and sid+total<=end before returning, ensuring the read range [sid, sid+sid_len) stays within the validated server buffer. False positive.
Finding #3 — Category B — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le16_to_cpu() line 2393 |
| Taint snippet | u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); |
| Tainted var | sid_len |
| Sink | memcpy() line 2417 (arg 2, role=size) |
| Sink snippet | memcpy(&posix->group, sid, sid_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same analysis as finding #1 but for the group SID. posix_info_sid_size() is called again with the updated sid pointer and the same end boundary. sid_len is again bounded to [8,68], and sizeof(posix->group)==sizeof(struct cifs_sid)==68. No OOB write possible.
Finding #4 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 2393 |
| Taint snippet | u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); |
| Tainted var | sid |
| Sink | memcpy() line 2417 (arg 1, role=pointer) |
| Sink snippet | memcpy(&posix->group, sid, sid_len); |
| Possibly guarded | no |
Dismissed: Same analysis as finding #2 but for the group SID pointer. After the owner SID memcpy, sid is advanced by owner's sid_len (validated <= end). posix_info_sid_size() then validates the group SID pointer and length against end. All accesses remain within the server buffer bounds established by the caller.
posix_info_parse() — fs/smb/client/smb2pdu.c BUG confidence=high
posix_info_parse() carefully validates that owner_sid and group_sid data fits within the source buffer [beg, end) via posix_info_sid_size(), which returns the total SID byte count. However, it never validates that owner_len or group_len fits within the DESTINATION fields out->owner and out->group. The destination types are fixed-size struct fields (likely struct smb_sid), but posix_info_sid_size() can return up to 1+1+6+4*15=68 bytes (subauth up to 15). If the destination struct fields are smaller than 68 bytes, a server can craft a SID with many sub-authorities to cause an OOB write into adjacent fields of the smb2_posix_info_parsed struct.
Finding #1 — Category B — BUG oob_write
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 5439 |
| Taint snippet | end = beg + le32_to_cpu(p->NextEntryOffset); |
| Tainted var | owner_len |
| Sink | memcpy() line 5483 (arg 2, role=size) |
| Sink snippet | memcpy(&out->owner, owner_sid, owner_len); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds in posix_info_parse or heap corruption when copying a server-crafted SID with 15 sub-authorities into out->owner if sizeof(out->owner) < 68
Fix: After posix_info_sid_size() returns owner_len, add: if (owner_len > sizeof(out->owner)) return -1; Similarly for group_len vs sizeof(out->group). This ensures the destination buffer is large enough before the memcpy.
CVE pattern: Server-controlled size used in memcpy without destination bounds check — similar pattern to various SMB2 response parsing OOB writes
Finding #2 — Category B — BUG oob_write
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 5439 |
| Taint snippet | end = beg + le32_to_cpu(p->NextEntryOffset); |
| Tainted var | group_len |
| Sink | memcpy() line 5484 (arg 2, role=size) |
| Sink snippet | memcpy(&out->group, group_sid, group_len); |
| Possibly guarded | yes (heuristic) |
| Server-supplied | yes |
| Check present | yes |
| Check sufficient | no |
Symptom: KASAN: slab-out-of-bounds in posix_info_parse or heap corruption when copying a server-crafted group SID with 15 sub-authorities into out->group if sizeof(out->group) < 68
Fix: After posix_info_sid_size() returns group_len, add: if (group_len > sizeof(out->group)) return -1; This ensures the destination buffer is large enough before the memcpy.
CVE pattern: Server-controlled size used in memcpy without destination bounds check — similar to SMB2 response parsing OOB writes
query_info() — fs/smb/client/smb2pdu.c FP confidence=high
The flagged value (le16_to_cpu(rsp->OutputBufferOffset)) is genuinely server-supplied from the SMB2 query info response. However, it is passed directly into smb2_validate_and_copy_iov(), which is itself a validation function: it calls smb2_validate_iov(offset, buffer_length, iov, minbufsize) before performing the memcpy. The memcpy inside smb2_validate_and_copy_iov() is the result of successful validation, not a vulnerable sink. The smb2_validate_iov call checks that offset + buffer_length does not exceed iov->iov_len and that the buffer is large enough, ensuring begin_of_buf and the subsequent memcpy(data, begin_of_buf, minbufsize) are safe. The pattern here is the classic validator-function false positive: the scanner flagged the memcpy inside the validator as a sink, but those accesses are protected by the validation logic immediately preceding them.
Finding #1 — Category A — cross-function via smb2_validate_and_copy_iov() — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le16_to_cpu() line 4011 |
| Taint snippet | rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), |
| Tainted var | le16_to_cpu(rsp->OutputBufferOffset) |
| Call site | line 4011 — passes le16_to_cpu(rsp->OutputBufferOffset) to smb2_validate_and_copy_iov() |
| Call snippet | rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), |
| Sink (in callee) | memcpy() line 3868 (arg 1, role=pointer) |
| Sink snippet | memcpy(data, begin_of_buf, minbufsize); |
| Possibly guarded | no |
Dismissed: smb2_validate_and_copy_iov() calls smb2_validate_iov(offset, buffer_length, iov, minbufsize) before executing the memcpy. smb2_validate_iov verifies that the offset and buffer_length are within the iov bounds and that the minimum buffer size is satisfied. Only if all checks pass does the code reach memcpy(data, begin_of_buf, minbufsize). No counterexample can be constructed because any out-of-range offset or length would cause smb2_validate_iov to return an error, preventing the memcpy. This is a false positive: the memcpy is inside the validation function, after its own internal guard.
smb2_parse_contexts() — fs/smb/client/smb2pdu.c FP confidence=high
The function has a careful multi-layer validation discipline: (1) pre-loop check ensures off+rem fits in iov_len; (2) the loop condition 'rem >= sizeof(*cc)' guarantees struct field reads are in-bounds; (3) per-iteration doff+dlen <= rem check bounds data; (4) noff+nlen <= doff check bounds name within rem; (5) check_sub_overflow on cc->Next maintains rem as a valid remaining-bytes invariant; (6) parse_posix_ctxt uses posix_info_sid_size with 'end' pointer to bound-check SID sizes before memcpy. All flagged accesses are protected by these guards.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 2445 |
| Taint snippet | rem = le32_to_cpu(rsp->CreateContextsLength); |
| Tainted var | rem |
| Loop | while_loop line 2454 |
| Sink snippet | while (rem >= sizeof(*cc)) { |
| Possibly guarded | no |
Dismissed: rem is validated against rsp_iov->iov_len before the loop (off+rem <= iov_len). The loop condition 'rem >= sizeof(*cc)' provides per-iteration bounds. check_sub_overflow(rem, off, &rem) on cc->Next keeps rem as valid remaining bytes. No counterexample possible: any value of rem that exceeds the buffer would be caught by the pre-loop check.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Pointer deref | cc->DataOffset line 2455 |
| Sink snippet | doff = le16_to_cpu(cc->DataOffset); |
| Possibly guarded | no |
Dismissed: The loop guard 'rem >= sizeof(*cc)' ensures at least sizeof(struct create_context) bytes are available at cc before any field is read. cc->DataOffset is within that struct, so the read is safe. Cannot construct a counterexample where the guard passes but the field access is OOB.
Finding #3 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Pointer deref | cc->DataLength line 2456 |
| Sink snippet | dlen = le32_to_cpu(cc->DataLength); |
| Possibly guarded | no |
Dismissed: Same as finding #2: loop guard ensures sizeof(*cc) bytes available, DataLength is a field within that struct. Protected.
Finding #4 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Pointer deref | cc->NameOffset line 2460 |
| Sink snippet | noff = le16_to_cpu(cc->NameOffset); |
| Possibly guarded | no |
Dismissed: Same as finding #2: loop guard ensures sizeof(*cc) bytes available, NameOffset is a field within that struct. Protected.
Finding #5 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Pointer deref | cc->NameLength line 2461 |
| Sink snippet | nlen = le16_to_cpu(cc->NameLength); |
| Possibly guarded | no |
Dismissed: Same as finding #2: loop guard ensures sizeof(*cc) bytes available, NameLength is a field within that struct. Protected.
Finding #6 — Category A — false positive
| Category | Cat A — server offset → pointer → memory op |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | name |
| Sink | memcmp() line 2477 (arg 0, role=pointer) |
| Sink snippet | if (posix && !memcmp(name, smb3_create_tag_posix, 16)) |
| Possibly guarded | no |
Dismissed: name = cc + noff. We have: noff + nlen <= doff (line 2462 check), and doff + dlen <= rem (line 2457 check), so noff + nlen <= rem. With nlen=16 for the memcmp case, name+16 is within the valid rem bytes from cc. Cannot construct a counterexample where these guards pass but memcmp reads beyond the buffer.
Finding #7 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Pointer deref | cc->Next line 2488 |
| Sink snippet | off = le32_to_cpu(cc->Next); |
| Possibly guarded | no |
Dismissed: cc->Next is read after all per-iteration data validation. The loop guard ensures sizeof(*cc) bytes are available at cc, so reading cc->Next (a field within the struct) is safe. check_sub_overflow(rem, off, &rem) then validates the Next offset doesn't exceed rem.
Finding #8 — Category B — cross-function via parse_posix_ctxt() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Call site | line 2478 — passes cc to parse_posix_ctxt() |
| Call snippet | parse_posix_ctxt(cc, buf, posix); |
| Sink (in callee) | memcpy() line 2409 (arg 2, role=size) |
| Sink snippet | memcpy(&posix->owner, sid, sid_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: In parse_posix_ctxt, beg = cc + DataOffset and end = beg + DataLength. These are bounded by the prior doff+dlen <= rem validation. posix_info_sid_size(sid, end) computes sid_len with 'end' as the upper bound and returns negative if the SID extends beyond 'end'. The early return on sid_len < 0 prevents the memcpy. The size argument to memcpy is thus bounded by [0, dlen]. No counterexample possible.
Finding #9 — Category B — cross-function via parse_posix_ctxt() — false positive
| Category | Cat B — server value → size/alloc argument |
|---|---|
| Taint source | le32_to_cpu() line 2444 |
| Taint snippet | off = le32_to_cpu(rsp->CreateContextsOffset); |
| Tainted var | cc |
| Call site | line 2478 — passes cc to parse_posix_ctxt() |
| Call snippet | parse_posix_ctxt(cc, buf, posix); |
| Sink (in callee) | memcpy() line 2417 (arg 2, role=size) |
| Sink snippet | memcpy(&posix->group, sid, sid_len); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same analysis as finding #8 for the group SID memcpy. posix_info_sid_size advances sid past the owner SID and validates the group SID against end, returning negative if invalid. The early return on sid_len < 0 prevents unsafe memcpy. Protected.
smb311_decode_neg_context() — fs/smb/client/smb2pdu.c FP confidence=high
The function has robust per-iteration bounds checking. The initial offset is validated (len_of_smb > offset), then len_of_ctxts = len_of_smb - offset. Each iteration checks len_of_ctxts >= sizeof(struct smb2_neg_context) before dereferencing pctx, ensuring all field accesses are within bounds. The loop count ctxt_cnt is irrelevant because the per-iteration break guard is sufficient — no matter how large ctxt_cnt is, the loop terminates when the remaining buffer is exhausted. All sub-context decoders also validate their DataLength fields before accessing flexible array members.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 975 |
| Taint snippet | unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); |
| Tainted var | ctxt_cnt |
| Loop | for_loop line 987 |
| Sink snippet | for (i = 0; i < ctxt_cnt; i++) { |
| Possibly guarded | no |
Dismissed: Per-iteration guard at line 990 breaks when len_of_ctxts < sizeof(struct smb2_neg_context), effectively capping iterations by available buffer space regardless of ctxt_cnt. Counterexample attempt: ctxt_cnt=65535, len_of_ctxts=0 → loop breaks immediately. No OOB possible.
Finding #2 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->DataLength line 995 |
| Sink snippet | + le16_to_cpu(pctx->DataLength); |
| Possibly guarded | no |
Dismissed: Before pctx->DataLength is read at line 995, line 990 ensures len_of_ctxts >= sizeof(struct smb2_neg_context), which means offset + sizeof(struct smb2_neg_context) <= len_of_smb. DataLength is within the struct header so the access is safe.
Finding #3 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1006 |
| Sink snippet | if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) |
| Possibly guarded | no |
Dismissed: Same protection as finding #2 — line 990 ensures the full smb2_neg_context header (including ContextType) is within bounds before pctx is dereferenced.
Finding #4 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1009 |
| Sink snippet | else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same ContextType access, same protection from line 990 guard. False positive.
Finding #5 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1012 |
| Sink snippet | else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same ContextType access, same protection from line 990 guard. False positive.
Finding #6 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1015 |
| Sink snippet | else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same ContextType access, same protection from line 990 guard. False positive.
Finding #7 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1017 |
| Sink snippet | else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same ContextType access, same protection from line 990 guard. False positive.
Finding #8 — Category E — false positive
| Category | Cat E — tainted pointer dereference (->field access beyond packet bounds) |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Pointer deref | pctx->ContextType line 1022 |
| Sink snippet | le16_to_cpu(pctx->ContextType)); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same ContextType access in the else branch (debug log), same protection from line 990 guard. False positive.
Finding #9 — Category F — cross-function via decode_compress_ctx() — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le32_to_cpu() line 974 |
| Taint snippet | unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); |
| Tainted var | pctx |
| Call site | line 1013 — passes pctx to decode_compress_ctx() |
| Call snippet | decode_compress_ctx(server, |
| Loop | for_loop line 866 |
| Sink snippet | for (i = 0; i < count; i++) { |
| Possibly guarded | yes (heuristic) |
Dismissed: decode_compress_ctx() validates count against ARRAY_SIZE(ctxt->CompressionAlgorithms) and checks len >= 8 + count * sizeof(__le16). The len was already bounded by the caller's clen <= len_of_ctxts check. The loop bound is properly validated before use. No OOB possible.
smb2_calc_signature() — fs/smb/client/smb2transport.c FP confidence=high
The static scanner has misidentified the taint flow. The finding claims that `sid` (a 64-bit value from le64_to_cpu) is silently truncated to 32-bit `rc`, but `sid` is only passed as an argument to `smb2_get_sign_key()`. The return value `rc` is the integer return code of that function call — it is NOT a truncation of `sid`. The scanner appears to have incorrectly propagated the taint from `sid` through the function call argument to the return value stored in `rc`. This is a classic taint analysis false positive where taint from a function argument is incorrectly attributed to the function's return value.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le64_to_cpu() line 220 |
| Taint snippet | rc = smb2_get_sign_key(server, sid, key); |
| Tainted var | rc |
| Truncation | line 220: 64 → 32-bit u32 |
| Sink snippet | rc = smb2_get_sign_key(server, sid, key); |
| Possibly guarded | no |
Dismissed: The taint flow is entirely fabricated by the scanner. `sid` (from le64_to_cpu(shdr->SessionId)) is indeed server-supplied and passed to `smb2_get_sign_key(server, sid, key)`. However, `rc` is the INTEGER RETURN CODE of that function, not a truncation of `sid`. The 64-bit `sid` value is passed as a u64 parameter; there is no truncation to 32-bit. The return value `rc` is the function's error code (an int), which is completely independent of the `sid` value. The scanner incorrectly propagated taint from the `sid` argument to the `rc` return value assignment. No counterexample can be constructed because no truncation actually occurs — the types are entirely separate variables with no arithmetic relationship at the flagged line.
smb2_seq_num_into_buf() — fs/smb/client/smb2transport.c FP confidence=high
The shdr pointer comes from a locally-constructed SMB2 request buffer (rqst->rq_iov[0].iov_base), not a server response. The CreditCharge field is written by the kernel when building outgoing requests. Additionally, the loop body only calls get_next_mid(server) which increments an internal counter — there is no buffer indexing or memory access whose bounds could be violated by the iteration count. The finding conflates 'loop iteration count' with 'buffer traversal', but this loop performs no memory traversal.
Finding #1 — Category F — false positive
| Category | Cat F — server value → loop iteration count |
|---|---|
| Taint source | le16_to_cpu() line 602 |
| Taint snippet | unsigned int i, num = le16_to_cpu(shdr->CreditCharge); |
| Tainted var | num |
| Loop | for_loop line 606 |
| Sink snippet | for (i = 1; i < num; i++) |
| Possibly guarded | no |
Dismissed: shdr is extracted from a locally-built SMB2 request (rqst->rq_iov[0].iov_base), so CreditCharge was set by the kernel, not received from a server. Furthermore, the loop body only calls get_next_mid(server) to advance a sequence number counter — it performs no buffer access, so there is no OOB memory hazard regardless of the iteration count. No counterexample for OOB access can be constructed because the loop does not index any buffer.
smb3_calc_signature() — fs/smb/client/smb2transport.c FP confidence=high
The finding misidentifies the taint flow. le64_to_cpu(shdr->SessionId) is passed as an argument to smb3_get_sign_key(), not assigned to 'rc'. The return value 'rc' is an int (error code) from smb3_get_sign_key(), not a truncation of the 64-bit SessionId. There is no truncation of the server-supplied SessionId into 'rc'; 'rc' receives the function's return value which is a standard kernel error code (int). The SessionId is passed as a u64 parameter to smb3_get_sign_key() — no truncation occurs at the call site either, since the parameter type of smb3_get_sign_key() accepts a 64-bit session ID. The scanner incorrectly traced the taint from le64_to_cpu() through the function call return assignment.
Finding #1 — Category H — false positive
| Category | Cat H — server value → narrow integer type (silent truncation) |
|---|---|
| Taint source | le64_to_cpu() line 475 |
| Taint snippet | rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); |
| Tainted var | rc |
| Truncation | line 475: 64 → 32-bit u32 |
| Sink snippet | rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); |
| Possibly guarded | no |
Dismissed: The scanner incorrectly attributed the taint of le64_to_cpu(shdr->SessionId) to 'rc'. In reality, rc receives the integer return value of smb3_get_sign_key(), which is a standard errno-style int, not a truncation of the 64-bit SessionId. The SessionId is passed as a u64 argument to smb3_get_sign_key() — no narrowing truncation occurs. The rc value is then checked with 'if (unlikely(rc))' which correctly handles all error conditions. No counterexample exists because rc never holds the SessionId value. This is a false positive caused by imprecise taint propagation through function call boundaries.
__release_mid() — fs/smb/client/transport.c FP confidence=high
All array accesses using smb_cmd are uniformly guarded by the check 'if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)' on line 68 (and again on line 98 for the second block). The arrays server->num_cmds[], server->slowest_cmd[], server->fastest_cmd[], server->time_per_cmd[], and server->smb2slowcmd[] are all dimensioned [NUMBER_OF_SMB2_COMMANDS]. Since smb_cmd is a __u16 (unsigned, non-negative) and is checked to be strictly less than NUMBER_OF_SMB2_COMMANDS before every array access, no out-of-bounds access is possible. The scanner flagged these as 'possibly guarded' and the guards are in fact complete and sufficient. No counterexample can be constructed: any smb_cmd >= NUMBER_OF_SMB2_COMMANDS causes an early exit from the if-block before any array indexing occurs.
Finding #1 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 69 |
| Sink snippet | if (atomic_read(&server->num_cmds[smb_cmd]) == 0) { |
| Possibly guarded | yes (heuristic) |
Dismissed: Guard at line 68: 'if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)' strictly bounds smb_cmd before all accesses on lines 69-79. Cannot construct a counterexample: any value >= NUMBER_OF_SMB2_COMMANDS skips the entire block.
Finding #2 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 70 |
| Sink snippet | server->slowest_cmd[smb_cmd] = roundtrip_time; |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 70. No counterexample possible.
Finding #3 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 71 |
| Sink snippet | server->fastest_cmd[smb_cmd] = roundtrip_time; |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 71. No counterexample possible.
Finding #4 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 73 |
| Sink snippet | if (server->slowest_cmd[smb_cmd] < roundtrip_time) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 73. No counterexample possible.
Finding #5 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 74 |
| Sink snippet | server->slowest_cmd[smb_cmd] = roundtrip_time; |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 74. No counterexample possible.
Finding #6 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 75 |
| Sink snippet | else if (server->fastest_cmd[smb_cmd] > roundtrip_time) |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 75. No counterexample possible.
Finding #7 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 76 |
| Sink snippet | server->fastest_cmd[smb_cmd] = roundtrip_time; |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 76. No counterexample possible.
Finding #8 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 78 |
| Sink snippet | cifs_stats_inc(&server->num_cmds[smb_cmd]); |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 78. No counterexample possible.
Finding #9 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 79 |
| Sink snippet | server->time_per_cmd[smb_cmd] += roundtrip_time; |
| Possibly guarded | yes (heuristic) |
Dismissed: Same guard as finding #1 protects line 79. No counterexample possible.
Finding #10 — Category C — false positive
| Category | Cat C — server value → array subscript |
|---|---|
| Taint source | le16_to_cpu() line 45 |
| Taint snippet | __u16 smb_cmd = le16_to_cpu(midEntry->command); |
| Tainted var | smb_cmd |
| Subscript | [] line 99 |
| Sink snippet | cifs_stats_inc(&server->smb2slowcmd[smb_cmd]); |
| Possibly guarded | yes (heuristic) |
Dismissed: Guard at line 98 ('if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)') strictly bounds smb_cmd before the access at line 99. No counterexample possible.