From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: David Howells <dhowells@redhat.com>,
Steve French <sfrench@samba.org>,
Paulo Alcantara <pc@manguebit.org>,
Enzo Matsumiya <ematsumiya@suse.de>,
linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org,
Steve French <stfrench@microsoft.com>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.18.y 4/5] cifs: SMB1 split: Separate out SMB1 decls into smb1proto.h
Date: Mon, 7 Sep 2026 07:47:26 -0400 [thread overview]
Message-ID: <20260907114727.2870618-4-sashal@kernel.org> (raw)
In-Reply-To: <20260907114727.2870618-1-sashal@kernel.org>
From: David Howells <dhowells@redhat.com>
[ Upstream commit 645427b7a6c59e0074df29bf939aec2e9d6f2819 ]
Separate out SMB1 declarations scriptedly into smb1proto.h. Script below:
#!/usr/bin/perl -w
use strict;
unless (@ARGV) {
die "Usage: $0 <c_file1> [<c_file2> ...]\n";
}
# Data tracking
my %funcs = (); # Func name => { func prototype }
my %headers = (); # Header filename => { header content }
my %c_files = (); # C filename => { ordered func list, header pref }
my %cmarkers = (); # C filename marker => { header filename it's in }
# Parse state
my $pathname = "-";
my $lineno = 0;
sub error(@) {
print STDERR $pathname, ":", $lineno, ": ", @_, "\n";
exit(1);
}
sub pad($) {
# Reindent the function arguments to line the arguments up with the char
# after the opening bracket on the func argument list
my ($lines) = @_;
return $lines if ($#{$lines} <= 0);
my $has_empty = 0;
for (my $i = 0; $i <= $#{$lines}; $i++) {
$lines->[$i] =~ s/^[ \t]+//;
$has_empty = 1 if ($lines->[$i] eq "");
}
if ($has_empty) {
my @clean = grep /.+/, @{$lines};
$lines = \@clean;
}
my $indlen = index($lines->[0], "(");
return $lines if ($indlen < 0);
my $indent = "";
$indlen++;
$indent .= "\t" x ($indlen / 8);
$indent .= " " x ($indlen % 8);
my @padded = ();
my $acc = "";
my $len = -$indlen;
for (my $i = 0; $i <= $#{$lines}; $i++) {
my $argument = $lines->[$i];
my $arglen = length($argument);
my $last = ($i == $#{$lines} ? 1 : 0);
if ($i == 0 ||
$i == 1) {
$acc .= $argument;
$acc .= ";" if ($last);
$len += $arglen + $last;
next;
}
if (!$acc) {
$acc = $indent . $argument;
$acc .= ";" if ($last);
$len += $arglen + $last;
next;
}
if ($indlen + $len + 1 + $arglen + $last > 79) {
push @padded, $acc;
$acc = $indent . $argument;
$acc .= ";" if ($last);
$len = $arglen + $last;
next;
}
$acc .= " " . $argument;
$acc .= ";" if ($last);
$len += 1 + $arglen + $last;
}
push @padded, $acc if ($acc);
return \@padded;
}
sub earliest(@) {
my $ret = -1;
foreach (@_) {
$ret = $_ if ($ret < 0 || ($_ >= 0 && $_ < $ret));
}
return $ret;
}
foreach my $file (@ARGV) {
# Open the file for reading.
next if $file =~ /trace[.]h$/;
next if $file =~ /smbdirect[.][ch]$/;
open my $fh, "<$file"
or die "Could not open file '$file'";
$pathname = $file;
$lineno = 0;
my $filename;
my @file_content = ();
my @copy = ();
my $state = 0;
my $qual = "";
my $type = "";
my $funcname = "";
my @funcdef = ();
my $bracket = 0;
my $comment = 0;
my $smb1 = 0;
my $header = 0;
my $inline = 0;
my $file_marker = "";
my $config = "";
my $c_file = 0;
$filename = $pathname;
$filename =~ s!.*/!!;
if ($file =~ m!.h$!) {
my %new_h_file = (
path => $pathname,
fname => $filename,
content => [],
);
$header = \%new_h_file;
$headers{$filename} = \%new_h_file;
} elsif ($file =~ m!.c$!) {
my %new_c_file = (
path => $pathname,
fname => $filename,
funcs => [],
);
$c_file = \%new_c_file;
$c_files{$filename} = \%new_c_file;
} else {
warn("Ignoring unexpected file $file\n");
next;
}
$smb1 = 1 if ($file =~ m!/smb1ops.c|/cifssmb.c|/cifstransport.c!);
foreach my $line (<$fh>) {
$lineno++;
chomp($line);
push @copy, $line;
if (!$line) {
# Blank line
push @file_content, @copy;
@copy = ();
next;
}
# Handle continuation or end of block comment. Look for C file
# prototype insertion point markers.
if ($comment) {
if ($line =~ m![*]/!) {
if ($comment == 2 && $file_marker) {
$cmarkers{$file_marker} = $file_marker;
push @copy, "#C_MARKER " . $file_marker;
$file_marker = 0;
}
$comment = 0;
} else {
$comment++;
if ($comment == 2 && $line =~ m! [*] ([a-z][a-z_0-9]*[.][c])$!) {
$file_marker = $1;
print("Found file marker ", $file_marker, " in ", $filename, "\n");
}
}
push @file_content, @copy;
@copy = ();
next;
}
# Check cpp directives, particularly looking for SMB1 bits
if ($line =~ /^[#]/) {
if ($header) {
if ($line =~ /ifdef.*(CONFIG_[A-Z0-9_])/) {
error("multiconfig") if $config;
$config = $1;
$smb1++ if ($config eq "CONFIG_CIFS_ALLOW_INSECURE_LEGACY");
} elsif ($line =~ /endif/) {
$smb1-- if ($config eq "CONFIG_CIFS_ALLOW_INSECURE_LEGACY");
$config = "";
}
}
push @file_content, @copy;
@copy = ();
next;
}
# Exclude interference in finding func names and return types
if ($line =~ /^[{]/ ||
$line =~ /##/ ||
$line =~ /^[_a-z0-9A-Z]+:$/ || # goto label
$line =~ /^do [{]/ ||
$line =~ m!^//!) {
push @file_content, @copy;
@copy = ();
next;
}
# Start of a block comment
if ($line =~ m!^/[*]!) {
$comment = 1 unless ($line =~ m![*]/!);
push @file_content, @copy;
@copy = ();
next;
}
# End of a braced section, such as a function implementation
if ($line =~ /^[}]/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
if ($line =~ /^typedef/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
# Extract function qualifiers. There may be multiple of these in more
# or less any order. Some of them cause the func to be skipped (e.g. inline).
if ($line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)\W/ ||
$line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)$/) {
error("Unexpected qualifier '$1'") if ($state != 0);
while ($line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)\W/ ||
$line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)$/) {
$qual .= " " if ($qual);
$qual .= $1;
$inline = 1 if ($1 eq "inline");
$inline = 1 if ($1 eq "__always_inline");
$line = substr($line, length($1));
$line =~ s/^\s+//;
}
}
if ($state == 0) {
# Extract what we assume to be the return type
if ($line =~ /^\s/) {
push @file_content, @copy;
@copy = ();
next;
}
while ($line =~ /^(unsigned|signed|bool|char|short|int|long|void|const|volatile|(struct|union|enum)\s+[_a-zA-Z][_a-zA-Z0-9]*|[*]|__init|__exit|__le16|__le32|__le64|__be16|__be32|__be64)/) {
$type .= " " if $type;
$type .= $1;
$line = substr($line, length($1));
$line =~ s/^\s+//;
}
if ($line =~ /^struct [{]/) {
# Ignore structure definitions
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
if (index($line, "=") >= 0) {
# Ignore assignments
$type = "";
$qual = "";
$funcname = "";
@funcdef = "";
push @file_content, @copy;
@copy = ();
next;
}
# Try and extract a function's type and name
while ($line =~ /(^[_a-zA-Z][_a-zA-Z0-9]*)/) {
my $name = $1;
$line = substr($line, length($name));
next if ($line =~ /^[{]/);
$line =~ s/^\s+//;
my $ch = substr($line, 0, 1);
last if ($ch eq "[" || $ch eq ";"); # Global variables
if ($ch eq "(") {
# Found the function name
$state = 1;
$line = substr($line, 1);
$funcname = $name;
my $tmp = $qual . $type . " " . $funcname . "(";
$tmp =~ s/[*] /*/;
push @funcdef, $tmp;
$bracket = 1;
last;
}
if ($type) {
last if (index($line, ";") >= 0 && index($line, "(") == -1);
error("Unexpected name '$name' after '$type'");
}
$type .= " " if $type;
$type .= $name;
if ($line =~ /^(\s*[*]+)/) {
my $ptr = $1;
$type .= $ptr;
$line = substr($line, length($ptr));
}
}
}
# Try and extract a function's argument list
my $from = 0;
if ($state == 1) {
while (1) {
my $o = index($line, "(", $from);
my $c = index($line, ")", $from);
my $m = index($line, ",", $from);
my $b = earliest($o, $c, $m);
if ($b < 0) {
push @funcdef, $line
unless ($line eq "");
last;
}
my $ch = substr($line, $b, 1);
# Push the arguments separately on to the list
if ($ch eq ",") {
push @funcdef, substr($line, 0, $b + 1);
$line = substr($line, $b + 1);
$from = 0;
} elsif ($ch eq "(") {
# Handle brackets in the argument list (e.g. function
# pointers)
$bracket++;
$from = $b + 1;
} elsif ($ch eq ")") {
$bracket--;
if ($bracket == 0) {
push @funcdef, substr($line, 0, $b + 1);
$line = substr($line, $b + 1);
$state = 2;
last;
}
$from = $b + 1;
}
}
}
if ($state == 2) {
$inline = 1 if ($qual =~ /inline/);
#print("QUAL $qual $type $funcname $inline ", $#funcdef, "\n");
if (!$header &&
$qual !~ /static/ &&
$funcname ne "__acquires" &&
$funcname ne "__releases" &&
$funcname ne "module_init" &&
$funcname ne "module_exit" &&
$funcname ne "module_param" &&
$funcname ne "module_param_call" &&
$funcname ne "PROC_FILE_DEFINE" &&
$funcname !~ /MODULE_/ &&
$funcname !~ /DEFINE_/) {
# Okay, we appear to have a function implementation
my $func;
my $dup = 0;
if (exists($funcs{$funcname})) {
$func = $funcs{$funcname};
if (exists $func->{body}) {
print("dup $funcname\n");
$dup = 1;
}
} else {
my %new_func = (
name => $funcname,
cond => "",
legacy => 0,
);
$func = \%new_func;
$funcs{$funcname} = $func;
$func->{body} = pad(\@funcdef);
}
$func->{body} = pad(\@funcdef);
$func->{legacy} = 1 if $smb1;
if ($funcname eq "cifs_inval_name_dfs_link_error") {
$func->{cond} = "#ifdef CONFIG_CIFS_DFS_UPCALL";
} elsif ($funcname eq "cifs_listxattr") {
$func->{cond} = "#ifdef CONFIG_CIFS_XATTR";
}
push @{$c_file->{funcs}}, $func
unless $dup;
} elsif (!$header || $inline) {
# Ignore inline function implementations and other weirdies
push @file_content, @copy;
} elsif ($header && !$inline) {
push @file_content, "#FUNCPROTO " . $funcname;
my $func;
if (exists($funcs{$funcname})) {
$func = $funcs{$funcname};
$func->{lineno} = $lineno;
$func->{pathname} = $pathname;
} else {
my %new_func = (
name => $funcname,
cond => "",
lineno => $lineno,
pathname => $pathname,
legacy => 0,
);
$func = \%new_func;
$funcs{$funcname} = $func;
}
$func->{legacy} = 1 if $smb1;
}
@funcdef = ();
$type = "";
$qual = "";
$funcname = "";
$inline = 0;
$state = 0;
@copy = ();
}
if ($line =~ /;/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
$state = 0;
push @file_content, @copy;
@copy = ();
}
}
close($fh);
if ($header) {
$header->{content} = \@file_content;
}
}
sub write_header($)
{
my ($header) = @_;
my $path = $header->{path};
my $legacy = 0;
$legacy = 1 if ($path =~ m!smb1proto[.]h!);
my @output = ();
foreach my $line (@{$header->{content}}) {
if ($line =~ "^[#]C_MARKER (.*)") {
my $file_marker = $cmarkers{$1};
my $c_file = $c_files{$file_marker};
print("Found $line\n");
foreach my $func (@{$c_file->{funcs}}) {
print("func ", $func->{name}, "\n");
push @output, @{$func->{body}};
}
next;
} elsif ($line =~ "^[#]FUNCPROTO ([_a-zA-Z0-9]+)") {
my $funcname = $1;
my $func = $funcs{$funcname};
if (!$func->{body}) {
print($func->{pathname}, ":", $func->{lineno}, ": '", $funcname,
"' dead prototype\n");
next;
}
if ($func->{legacy} == $legacy) {
#push @output, $line;
push @output, @{$func->{body}};
}
} else {
push @output, $line;
}
}
open my $fh, ">$path"
or die "Could not open file '$path' for writing";
foreach my $f (@output) {
print($fh $f, "\n") or die $path;
}
close($fh) or die $path;
}
foreach my $h (keys(%headers)) {
write_header($headers{$h});
}
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Enzo Matsumiya <ematsumiya@suse.de>
cc: linux-cifs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-kernel@vger.kernel.org
Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
[ sashal: Reduced backport -- upstream 645427b7a6c59 touches 3 file(s), this
backport carries 3. Not backported here:
fs/smb/client/cifsproto.h
fs/smb/client/fscache.h
This note is generated from the file lists only; see the resolution record
for the reasoning. ]
Stable-dep-of: 6343c1da5619 ("smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/smb/client/smb1ops.c | 171 ----------------------------------
fs/smb/client/smb1proto.h | 2 +
fs/smb/client/smb1transport.c | 171 ++++++++++++++++++++++++++++++++++
3 files changed, 173 insertions(+), 171 deletions(-)
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index ca8f3dd7ff63b..5a500a5e138c1 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -238,146 +238,6 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
return mid;
}
-/*
- return codes:
- 0 not a transact2, or all data present
- >0 transact2 with that much data missing
- -EINVAL invalid transact2
- */
-static int
-check2ndT2(char *buf)
-{
- struct smb_hdr *pSMB = (struct smb_hdr *)buf;
- struct smb_t2_rsp *pSMBt;
- int remaining;
- __u16 total_data_size, data_in_this_rsp;
-
- if (pSMB->Command != SMB_COM_TRANSACTION2)
- return 0;
-
- /* check for plausible wct, bcc and t2 data and parm sizes */
- /* check for parm and data offset going beyond end of smb */
- if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
- cifs_dbg(FYI, "Invalid transact2 word count\n");
- return -EINVAL;
- }
-
- pSMBt = (struct smb_t2_rsp *)pSMB;
-
- total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
- data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
-
- if (total_data_size == data_in_this_rsp)
- return 0;
- else if (total_data_size < data_in_this_rsp) {
- cifs_dbg(FYI, "total data %d smaller than data in frame %d\n",
- total_data_size, data_in_this_rsp);
- return -EINVAL;
- }
-
- remaining = total_data_size - data_in_this_rsp;
-
- cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n",
- remaining);
- if (total_data_size > CIFSMaxBufSize) {
- cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n",
- total_data_size, CIFSMaxBufSize);
- return -EINVAL;
- }
- return remaining;
-}
-
-static int
-coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
-{
- struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
- struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr;
- char *data_area_of_tgt;
- char *data_area_of_src;
- int remaining;
- unsigned int byte_count, total_in_tgt;
- __u16 tgt_total_cnt, src_total_cnt, total_in_src;
-
- src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount);
- tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
-
- if (tgt_total_cnt != src_total_cnt)
- cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n",
- src_total_cnt, tgt_total_cnt);
-
- total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
-
- remaining = tgt_total_cnt - total_in_tgt;
-
- if (remaining < 0) {
- cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n",
- tgt_total_cnt, total_in_tgt);
- return -EPROTO;
- }
-
- if (remaining == 0) {
- /* nothing to do, ignore */
- cifs_dbg(FYI, "no more data remains\n");
- return 0;
- }
-
- total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount);
- if (remaining < total_in_src)
- cifs_dbg(FYI, "transact2 2nd response contains too much data\n");
-
- /* find end of first SMB data area */
- data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
- get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
-
- /* validate target area */
- data_area_of_src = (char *)&pSMBs->hdr.Protocol +
- get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
-
- data_area_of_tgt += total_in_tgt;
-
- total_in_tgt += total_in_src;
- /* is the result too big for the field? */
- if (total_in_tgt > USHRT_MAX) {
- cifs_dbg(FYI, "coalesced DataCount too large (%u)\n",
- total_in_tgt);
- return -EPROTO;
- }
- put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
-
- /* fix up the BCC */
- byte_count = get_bcc(target_hdr);
- byte_count += total_in_src;
- /* is the result too big for the field? */
- if (byte_count > USHRT_MAX) {
- cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count);
- return -EPROTO;
- }
- put_bcc(byte_count, target_hdr);
-
- byte_count = be32_to_cpu(target_hdr->smb_buf_length);
- byte_count += total_in_src;
- /* don't allow buffer to overflow */
- if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
- byte_count);
- return -ENOBUFS;
- }
- target_hdr->smb_buf_length = cpu_to_be32(byte_count);
-
- /* copy second buffer into end of first buffer */
- memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
-
- if (remaining != total_in_src) {
- /* more responses to go */
- cifs_dbg(FYI, "waiting for more secondary responses\n");
- return 1;
- }
-
- /* we are done */
- cifs_dbg(FYI, "found the last secondary response\n");
- return 0;
-}
-
static void
cifs_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
@@ -386,37 +246,6 @@ cifs_downgrade_oplock(struct TCP_Server_Info *server,
cifs_set_oplock_level(cinode, oplock);
}
-static bool
-cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
- char *buf, int malformed)
-{
- if (malformed)
- return false;
- if (check2ndT2(buf) <= 0)
- return false;
- mid->multiRsp = true;
- if (mid->resp_buf) {
- /* merge response - fix up 1st*/
- malformed = coalesce_t2(buf, mid->resp_buf);
- if (malformed > 0)
- return true;
- /* All parts received or packet is malformed. */
- mid->multiEnd = true;
- dequeue_mid(mid, malformed);
- return true;
- }
- if (!server->large_buf) {
- /*FIXME: switch to already allocated largebuf?*/
- cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n");
- } else {
- /* Have first buffer */
- mid->resp_buf = buf;
- mid->large_buf = true;
- server->bigbuf = NULL;
- }
- return true;
-}
-
static bool
cifs_need_neg(struct TCP_Server_Info *server)
{
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index a73213f6a2aa1..8dfc6976ceba1 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -33,6 +33,8 @@ extern struct smb_version_values smb1_values;
/*
* smb1transport.c
*/
+bool cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ char *buf, int malformed);
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
#endif /* _SMB1PROTO_H */
diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c
index bafc5181e0292..f7b00bcb9df1a 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -563,3 +563,174 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return -ERESTARTSYS;
return rc;
}
+
+/*
+ return codes:
+ 0 not a transact2, or all data present
+ >0 transact2 with that much data missing
+ -EINVAL invalid transact2
+ */
+static int
+check2ndT2(char *buf)
+{
+ struct smb_hdr *pSMB = (struct smb_hdr *)buf;
+ struct smb_t2_rsp *pSMBt;
+ int remaining;
+ __u16 total_data_size, data_in_this_rsp;
+
+ if (pSMB->Command != SMB_COM_TRANSACTION2)
+ return 0;
+
+ /* check for plausible wct, bcc and t2 data and parm sizes */
+ /* check for parm and data offset going beyond end of smb */
+ if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
+ cifs_dbg(FYI, "Invalid transact2 word count\n");
+ return -EINVAL;
+ }
+
+ pSMBt = (struct smb_t2_rsp *)pSMB;
+
+ total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
+ data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
+
+ if (total_data_size == data_in_this_rsp)
+ return 0;
+ else if (total_data_size < data_in_this_rsp) {
+ cifs_dbg(FYI, "total data %d smaller than data in frame %d\n",
+ total_data_size, data_in_this_rsp);
+ return -EINVAL;
+ }
+
+ remaining = total_data_size - data_in_this_rsp;
+
+ cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n",
+ remaining);
+ if (total_data_size > CIFSMaxBufSize) {
+ cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n",
+ total_data_size, CIFSMaxBufSize);
+ return -EINVAL;
+ }
+ return remaining;
+}
+
+static int
+coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
+{
+ struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
+ struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr;
+ char *data_area_of_tgt;
+ char *data_area_of_src;
+ int remaining;
+ unsigned int byte_count, total_in_tgt;
+ __u16 tgt_total_cnt, src_total_cnt, total_in_src;
+
+ src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount);
+ tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
+
+ if (tgt_total_cnt != src_total_cnt)
+ cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n",
+ src_total_cnt, tgt_total_cnt);
+
+ total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
+
+ remaining = tgt_total_cnt - total_in_tgt;
+
+ if (remaining < 0) {
+ cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n",
+ tgt_total_cnt, total_in_tgt);
+ return -EPROTO;
+ }
+
+ if (remaining == 0) {
+ /* nothing to do, ignore */
+ cifs_dbg(FYI, "no more data remains\n");
+ return 0;
+ }
+
+ total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount);
+ if (remaining < total_in_src)
+ cifs_dbg(FYI, "transact2 2nd response contains too much data\n");
+
+ /* find end of first SMB data area */
+ data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
+ get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
+
+ /* validate target area */
+ data_area_of_src = (char *)&pSMBs->hdr.Protocol +
+ get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
+
+ data_area_of_tgt += total_in_tgt;
+
+ total_in_tgt += total_in_src;
+ /* is the result too big for the field? */
+ if (total_in_tgt > USHRT_MAX) {
+ cifs_dbg(FYI, "coalesced DataCount too large (%u)\n",
+ total_in_tgt);
+ return -EPROTO;
+ }
+ put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
+
+ /* fix up the BCC */
+ byte_count = get_bcc(target_hdr);
+ byte_count += total_in_src;
+ /* is the result too big for the field? */
+ if (byte_count > USHRT_MAX) {
+ cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count);
+ return -EPROTO;
+ }
+ put_bcc(byte_count, target_hdr);
+
+ byte_count = be32_to_cpu(target_hdr->smb_buf_length);
+ byte_count += total_in_src;
+ /* don't allow buffer to overflow */
+ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
+ byte_count);
+ return -ENOBUFS;
+ }
+ target_hdr->smb_buf_length = cpu_to_be32(byte_count);
+
+ /* copy second buffer into end of first buffer */
+ memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
+
+ if (remaining != total_in_src) {
+ /* more responses to go */
+ cifs_dbg(FYI, "waiting for more secondary responses\n");
+ return 1;
+ }
+
+ /* we are done */
+ cifs_dbg(FYI, "found the last secondary response\n");
+ return 0;
+}
+
+bool
+cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ char *buf, int malformed)
+{
+ if (malformed)
+ return false;
+ if (check2ndT2(buf) <= 0)
+ return false;
+ mid->multiRsp = true;
+ if (mid->resp_buf) {
+ /* merge response - fix up 1st*/
+ malformed = coalesce_t2(buf, mid->resp_buf);
+ if (malformed > 0)
+ return true;
+ /* All parts received or packet is malformed. */
+ mid->multiEnd = true;
+ dequeue_mid(mid, malformed);
+ return true;
+ }
+ if (!server->large_buf) {
+ /*FIXME: switch to already allocated largebuf?*/
+ cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n");
+ } else {
+ /* Have first buffer */
+ mid->resp_buf = buf;
+ mid->large_buf = true;
+ server->bigbuf = NULL;
+ }
+ return true;
+}
--
2.53.0
next prev parent reply other threads:[~2026-09-07 11:47 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <2026090302-bronco-shame-0d3f@gregkh>
2026-09-07 11:47 ` [PATCH 6.18.y 1/5] cifs: Remove dead function prototypes Sasha Levin
2026-09-07 11:47 ` [PATCH 6.18.y 2/5] cifs: Scripted clean up fs/smb/client/fscache.h Sasha Levin
2026-09-07 11:47 ` [PATCH 6.18.y 3/5] cifs: SMB1 split: Create smb1proto.h for SMB1 declarations Sasha Levin
2026-09-07 11:47 ` Sasha Levin [this message]
[not found] <2026090302-harddisk-sinless-6dc1@gregkh>
2026-09-07 11:47 ` [PATCH 6.18.y 1/5] cifs: Remove dead function prototypes Sasha Levin
2026-09-07 11:47 ` [PATCH 6.18.y 4/5] cifs: SMB1 split: Separate out SMB1 decls into smb1proto.h Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260907114727.2870618-4-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=dhowells@redhat.com \
--cc=ematsumiya@suse.de \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pc@manguebit.org \
--cc=sfrench@samba.org \
--cc=stable@vger.kernel.org \
--cc=stfrench@microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.