@@ -2236,17 +2236,18 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
22362236 posix -> nlink , posix -> mode , posix -> reparse_tag );
22372237}
22382238
2239- void
2240- smb2_parse_contexts ( struct TCP_Server_Info * server ,
2241- struct smb2_create_rsp * rsp ,
2242- unsigned int * epoch , char * lease_key , __u8 * oplock ,
2243- struct smb2_file_all_info * buf ,
2244- struct create_posix_rsp * posix )
2239+ int smb2_parse_contexts ( struct TCP_Server_Info * server ,
2240+ struct kvec * rsp_iov ,
2241+ unsigned int * epoch ,
2242+ char * lease_key , __u8 * oplock ,
2243+ struct smb2_file_all_info * buf ,
2244+ struct create_posix_rsp * posix )
22452245{
2246- char * data_offset ;
2246+ struct smb2_create_rsp * rsp = rsp_iov -> iov_base ;
22472247 struct create_context * cc ;
2248- unsigned int next ;
2249- unsigned int remaining ;
2248+ size_t rem , off , len ;
2249+ size_t doff , dlen ;
2250+ size_t noff , nlen ;
22502251 char * name ;
22512252 static const char smb3_create_tag_posix [] = {
22522253 0x93 , 0xAD , 0x25 , 0x50 , 0x9C ,
@@ -2255,45 +2256,63 @@ smb2_parse_contexts(struct TCP_Server_Info *server,
22552256 };
22562257
22572258 * oplock = 0 ;
2258- data_offset = (char * )rsp + le32_to_cpu (rsp -> CreateContextsOffset );
2259- remaining = le32_to_cpu (rsp -> CreateContextsLength );
2260- cc = (struct create_context * )data_offset ;
2259+
2260+ off = le32_to_cpu (rsp -> CreateContextsOffset );
2261+ rem = le32_to_cpu (rsp -> CreateContextsLength );
2262+ if (check_add_overflow (off , rem , & len ) || len > rsp_iov -> iov_len )
2263+ return - EINVAL ;
2264+ cc = (struct create_context * )((u8 * )rsp + off );
22612265
22622266 /* Initialize inode number to 0 in case no valid data in qfid context */
22632267 if (buf )
22642268 buf -> IndexNumber = 0 ;
22652269
2266- while (remaining >= sizeof (struct create_context )) {
2267- name = le16_to_cpu (cc -> NameOffset ) + (char * )cc ;
2268- if (le16_to_cpu (cc -> NameLength ) == 4 &&
2269- strncmp (name , SMB2_CREATE_REQUEST_LEASE , 4 ) == 0 )
2270- * oplock = server -> ops -> parse_lease_buf (cc , epoch ,
2271- lease_key );
2272- else if (buf && (le16_to_cpu (cc -> NameLength ) == 4 ) &&
2273- strncmp (name , SMB2_CREATE_QUERY_ON_DISK_ID , 4 ) == 0 )
2274- parse_query_id_ctxt (cc , buf );
2275- else if ((le16_to_cpu (cc -> NameLength ) == 16 )) {
2276- if (posix &&
2277- memcmp (name , smb3_create_tag_posix , 16 ) == 0 )
2270+ while (rem >= sizeof (* cc )) {
2271+ doff = le16_to_cpu (cc -> DataOffset );
2272+ dlen = le32_to_cpu (cc -> DataLength );
2273+ if (check_add_overflow (doff , dlen , & len ) || len > rem )
2274+ return - EINVAL ;
2275+
2276+ noff = le16_to_cpu (cc -> NameOffset );
2277+ nlen = le16_to_cpu (cc -> NameLength );
2278+ if (noff + nlen >= doff )
2279+ return - EINVAL ;
2280+
2281+ name = (char * )cc + noff ;
2282+ switch (nlen ) {
2283+ case 4 :
2284+ if (!strncmp (name , SMB2_CREATE_REQUEST_LEASE , 4 )) {
2285+ * oplock = server -> ops -> parse_lease_buf (cc , epoch ,
2286+ lease_key );
2287+ } else if (buf &&
2288+ !strncmp (name , SMB2_CREATE_QUERY_ON_DISK_ID , 4 )) {
2289+ parse_query_id_ctxt (cc , buf );
2290+ }
2291+ break ;
2292+ case 16 :
2293+ if (posix && !memcmp (name , smb3_create_tag_posix , 16 ))
22782294 parse_posix_ctxt (cc , buf , posix );
2295+ break ;
2296+ default :
2297+ cifs_dbg (FYI , "%s: unhandled context (nlen=%zu dlen=%zu)\n" ,
2298+ __func__ , nlen , dlen );
2299+ if (IS_ENABLED (CONFIG_CIFS_DEBUG2 ))
2300+ cifs_dump_mem ("context data: " , cc , dlen );
2301+ break ;
22792302 }
2280- /* else {
2281- cifs_dbg(FYI, "Context not matched with len %d\n",
2282- le16_to_cpu(cc->NameLength));
2283- cifs_dump_mem("Cctxt name: ", name, 4);
2284- } */
2285-
2286- next = le32_to_cpu (cc -> Next );
2287- if (!next )
2303+
2304+ off = le32_to_cpu (cc -> Next );
2305+ if (!off )
22882306 break ;
2289- remaining -= next ;
2290- cc = (struct create_context * )((char * )cc + next );
2307+ if (check_sub_overflow (rem , off , & rem ))
2308+ return - EINVAL ;
2309+ cc = (struct create_context * )((u8 * )cc + off );
22912310 }
22922311
22932312 if (rsp -> OplockLevel != SMB2_OPLOCK_LEVEL_LEASE )
22942313 * oplock = rsp -> OplockLevel ;
22952314
2296- return ;
2315+ return 0 ;
22972316}
22982317
22992318static int
@@ -3124,8 +3143,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
31243143 }
31253144
31263145
3127- smb2_parse_contexts (server , rsp , & oparms -> fid -> epoch ,
3128- oparms -> fid -> lease_key , oplock , buf , posix );
3146+ rc = smb2_parse_contexts (server , & rsp_iov , & oparms -> fid -> epoch ,
3147+ oparms -> fid -> lease_key , oplock , buf , posix );
31293148creat_exit :
31303149 SMB2_open_free (& rqst );
31313150 free_rsp_buf (resp_buftype , rsp );
0 commit comments