commit ed0d9bd45e4d55cb3ca600c70fa26c6b7bf9bf05
Author: Michael Buckley <michael@panic.com>
Date:   Wed Dec 5 10:35:19 2018 -0800

    Add a required_size parameter to sftp_packet_require et. al. to require callers of these functions to handle packets that are too short.

Index: src/sftp.c
===================================================================
--- src/sftp.c.orig
+++ src/sftp.c
@@ -340,11 +340,15 @@ sftp_packet_ask(LIBSSH2_SFTP *sftp, unsi
 static int
 sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type,
                     uint32_t request_id, unsigned char **data,
-                    size_t *data_len)
+                    size_t *data_len, size_t required_size)
 {
     LIBSSH2_SESSION *session = sftp->channel->session;
     int rc;
 
+    if (data == NULL || data_len == NULL || required_size == 0) {
+        return LIBSSH2_ERROR_BAD_USE;
+    }
+
     _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld",
                    (int) packet_type, request_id);
 
@@ -352,6 +356,11 @@ sftp_packet_require(LIBSSH2_SFTP *sftp,
         /* The right packet was available in the packet brigade */
         _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
                        (int) packet_type);
+
+        if (*data_len < required_size) {
+            return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+        }
+
         return LIBSSH2_ERROR_NONE;
     }
 
@@ -368,6 +377,11 @@ sftp_packet_require(LIBSSH2_SFTP *sftp,
             /* The right packet was available in the packet brigade */
             _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
                            (int) packet_type);
+
+            if (*data_len < required_size) {
+                return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+            }
+
             return LIBSSH2_ERROR_NONE;
         }
     }
@@ -383,11 +397,15 @@ static int
 sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses,
                      const unsigned char *valid_responses,
                      uint32_t request_id, unsigned char **data,
-                     size_t *data_len)
+                     size_t *data_len, size_t required_size)
 {
     int i;
     int rc;
 
+    if (data == NULL || data_len == NULL || required_size == 0) {
+        return LIBSSH2_ERROR_BAD_USE;
+    }
+
     /* If no timeout is active, start a new one */
     if (sftp->requirev_start == 0)
         sftp->requirev_start = time(NULL);
@@ -401,6 +419,11 @@ sftp_packet_requirev(LIBSSH2_SFTP *sftp,
                  * the timeout is not active
                  */
                 sftp->requirev_start = 0;
+
+                if (*data_len < required_size) {
+                    return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+                }
+
                 return LIBSSH2_ERROR_NONE;
             }
         }
@@ -475,36 +498,65 @@ sftp_attr2bin(unsigned char *p, const LI
 /* sftp_bin2attr
  */
 static int
-sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p)
+sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p, size_t data_len)
 {
     const unsigned char *s = p;
 
-    memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-    attrs->flags = _libssh2_ntohu32(s);
-    s += 4;
+    if (data_len >= 4) {
+        memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+        attrs->flags = _libssh2_ntohu32(s);
+        s += 4;
+        data_len -= 4;
+    }
+    else {
+        return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+    }
 
     if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
-        attrs->filesize = _libssh2_ntohu64(s);
-        s += 8;
+        if (data_len >= 8) {
+            attrs->filesize = _libssh2_ntohu64(s);
+            s += 8;
+            data_len -= 8;
+        }
+        else {
+            return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+        }
     }
 
     if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
-        attrs->uid = _libssh2_ntohu32(s);
-        s += 4;
-        attrs->gid = _libssh2_ntohu32(s);
-        s += 4;
+        if (data_len >= 8) {
+            attrs->uid = _libssh2_ntohu32(s);
+            s += 4;
+            attrs->gid = _libssh2_ntohu32(s);
+            s += 4;
+            data_len -= 8;
+        }
+        else {
+            return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+        }
     }
 
     if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
-        attrs->permissions = _libssh2_ntohu32(s);
-        s += 4;
+        if (data_len >= 4) {
+            attrs->permissions = _libssh2_ntohu32(s);
+            s += 4;
+            data_len -= 4;
+        }
+        else {
+            return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+        }
     }
 
     if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
-        attrs->atime = _libssh2_ntohu32(s);
-        s += 4;
-        attrs->mtime = _libssh2_ntohu32(s);
-        s += 4;
+        if (data_len >= 8) {
+            attrs->atime = _libssh2_ntohu32(s);
+            s += 4;
+            attrs->mtime = _libssh2_ntohu32(s);
+            s += 4;
+        }
+        else {
+            return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+        }
     }
 
     return (s - p);
@@ -675,21 +727,25 @@ static LIBSSH2_SFTP *sftp_init(LIBSSH2_S
     }
 
     rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
-                             0, &data, &data_len);
+                             0, &data, &data_len, 5);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
-        _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
-                       "Would block waiting for response from SFTP subsystem");
-        return NULL;
-    } else if (rc) {
-        _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
-                       "Timeout waiting for response from SFTP subsystem");
-        goto sftp_init_error;
-    }
-    if (data_len < 5) {
-        _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+	_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+		       "Would block receiving SSH_FXP_VERSION");
+	return NULL;
+    }
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	if (data_len > 0) {
+	    LIBSSH2_FREE(session, data);
+	}
+	_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
                        "Invalid SSH_FXP_VERSION response");
         goto sftp_init_error;
     }
+    else if (rc) {
+	_libssh2_error(session, rc,
+		       "Timeout waiting for response from SFTP subsystem");
+	goto sftp_init_error;
+    }
 
     s = data + 1;
     sftp_handle->version = _libssh2_ntohu32(s);
@@ -949,12 +1005,20 @@ sftp_open(LIBSSH2_SFTP *sftp, const char
             { SSH_FXP_HANDLE, SSH_FXP_STATUS };
         rc = sftp_packet_requirev(sftp, 2, fopen_responses,
                                   sftp->open_request_id, &data,
-                                  &data_len);
+                                  &data_len, 1);
         if (rc == LIBSSH2_ERROR_EAGAIN) {
             _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
                            "Would block waiting for status message");
             return NULL;
         }
+        else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+            if (data_len > 0) {
+                LIBSSH2_FREE(session, data);
+            }
+            _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                           "Response too small");
+            return NULL;
+        }
         sftp->open_state = libssh2_NB_state_idle;
         if (rc) {
             _libssh2_error(session, rc, "Timeout waiting for status message");
@@ -985,12 +1049,20 @@ sftp_open(LIBSSH2_SFTP *sftp, const char
                 /* silly situation, but check for a HANDLE */
                 rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
                                          sftp->open_request_id, &data,
-                                         &data_len);
+                                         &data_len, 10);
                 if(rc == LIBSSH2_ERROR_EAGAIN) {
                     /* go back to sent state and wait for something else */
                     sftp->open_state = libssh2_NB_state_sent;
                     return NULL;
                 }
+                else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+                    if (data_len > 0) {
+                        LIBSSH2_FREE(session, data);
+                    }
+                    _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                                   "Too small FXP_HANDLE");
+                    return NULL;
+                }
                 else if(!rc)
                     /* we got the handle so this is not a bad situation */
                     badness = 0;
@@ -1089,6 +1161,7 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HA
     size_t total_read = 0;
     struct _libssh2_sftp_handle_file_data *filep =
         &handle->u.file;
+     size_t bytes_in_buffer = 0;
 
     /* Number of bytes asked for that haven't been acked yet */
     size_t already = (filep->offset_sent - filep->offset);
@@ -1128,6 +1201,11 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HA
         uint32_t packet_len = (uint32_t)handle->handle_len + 25;
         uint32_t request_id;
 
+	if(size < buffer_size)
+	    size = buffer_size;
+	if(size > MAX_SFTP_READ_SIZE)
+	    size = MAX_SFTP_READ_SIZE;
+
         chunk = LIBSSH2_ALLOC(session, packet_len +
                               sizeof(struct sftp_pipeline_chunk));
         if (!chunk)
@@ -1199,13 +1277,35 @@ static ssize_t sftp_read(LIBSSH2_SFTP_HA
             SSH_FXP_DATA, SSH_FXP_STATUS
         };
 
-        if(chunk->lefttosend)
+        if(chunk->lefttosend) {
             /* if the chunk still has data left to send, we shouldn't wait for
                an ACK for it just yet */
+	    if(bytes_in_buffer > 0) {
+		return bytes_in_buffer;
+	    }
+	    else {
+		/* we should never reach this point */
+		return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+				      "sftp_read() internal error");
+	    }
             break;
+	}
 
         rc = sftp_packet_requirev(sftp, 2, read_responses,
-                                  chunk->request_id, &data, &data_len);
+                                  chunk->request_id, &data, &data_len, 9);
+	if (rc == LIBSSH2_ERROR_EAGAIN && bytes_in_buffer != 0) {
+	    /* do not return EAGAIN if we have already
+	     * written data into the buffer */
+	    return bytes_in_buffer;
+	}
+
+	if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	    if (data_len > 0) {
+		LIBSSH2_FREE(session, data);
+	    }
+	    return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+				  "Response too small");
+	}
         if (rc == LIBSSH2_ERROR_EAGAIN) {
             eagain++;
             break;
@@ -1382,7 +1482,7 @@ static ssize_t sftp_readdir(LIBSSH2_SFTP
             if (attrs)
                 memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
 
-            s += sftp_bin2attr(attrs ? attrs : &attrs_dummy, s);
+            s += sftp_bin2attr(attrs ? attrs : &attrs_dummy, s, 32);
 
             handle->u.dir.next_name = (char *) s;
           end:
@@ -1437,9 +1537,16 @@ static ssize_t sftp_readdir(LIBSSH2_SFTP
 
     retcode = sftp_packet_requirev(sftp, 2, read_responses,
                                    sftp->readdir_request_id, &data,
-                                   &data_len);
+                                   &data_len, 9);
     if (retcode == LIBSSH2_ERROR_EAGAIN)
         return retcode;
+    else if (retcode == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+            return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                                  "Status message too short");
+    }
     else if (retcode) {
         sftp->readdir_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
@@ -1655,8 +1762,16 @@ static ssize_t sftp_write(LIBSSH2_SFTP_H
 
         /* we check the packets in order */
         rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
-                                 chunk->request_id, &data, &data_len);
-        if (rc == LIBSSH2_ERROR_EAGAIN) {
+                                 chunk->request_id, &data, &data_len, 9);
+	if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	    if (data_len > 0) {
+		LIBSSH2_FREE(session, data);
+	    }
+	    return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+				  "FXP write packet too short");
+	}
+	
+        if ((rc == LIBSSH2_ERROR_EAGAIN) || (rc < 0)) {
             eagain++;
             break;
         }
@@ -1809,10 +1924,17 @@ static int sftp_fstat(LIBSSH2_SFTP_HANDL
 
     rc = sftp_packet_requirev(sftp, 2, fstat_responses,
                               sftp->fstat_request_id, &data,
-                              &data_len);
-    if (rc == LIBSSH2_ERROR_EAGAIN) {
+                              &data_len, 9);
+    if (rc == LIBSSH2_ERROR_EAGAIN)
         return rc;
-    } else if (rc) {
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	if (data_len > 0) {
+	    LIBSSH2_FREE(session, data);
+	}
+	return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+			      "SFTP fstat packet too short");
+    }
+    else if (rc) {
         sftp->fstat_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -1834,7 +1956,12 @@ static int sftp_fstat(LIBSSH2_SFTP_HANDL
         }
     }
 
-    sftp_bin2attr(attrs, data + 5);
+    if (sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
+        LIBSSH2_FREE(session, data);
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "Attributes too short in SFTP fstat");
+    }
+
     LIBSSH2_FREE(session, data);
 
     return 0;
@@ -1981,11 +2108,19 @@ sftp_close_handle(LIBSSH2_SFTP_HANDLE *h
     if (handle->close_state == libssh2_NB_state_sent) {
         rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
                                  handle->close_request_id, &data,
-                                 &data_len);
+                                 &data_len, 9);
         if (rc == LIBSSH2_ERROR_EAGAIN) {
             return rc;
-        } else if (rc) {
-            handle->close_state = libssh2_NB_state_idle;
+	} else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	    if (data_len > 0) {
+		LIBSSH2_FREE(session, data);
+	    }
+	    data = NULL;
+	    _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+			    "Packet too short in FXP_CLOSE command");
+	}
+	else if (rc) {
+	    handle->close_state = libssh2_NB_state_idle;	    
             return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                                   "Timeout waiting for status message");
         }
@@ -2097,10 +2232,17 @@ static int sftp_unlink(LIBSSH2_SFTP *sft
 
     rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
                              sftp->unlink_request_id, &data,
-                             &data_len);
+                             &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
     }
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP unlink packet too short");
+    }
     else if (rc) {
         sftp->unlink_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
@@ -2208,10 +2350,18 @@ static int sftp_rename(LIBSSH2_SFTP *sft
 
     rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
                              sftp->rename_request_id, &data,
-                             &data_len);
+                             &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
-    } else if (rc) {
+    }
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP rename packet too short");
+    }
+    else if (rc) {
         sftp->rename_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2331,10 +2481,17 @@ static int sftp_fstatvfs(LIBSSH2_SFTP_HA
     }
 
     rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY,
-                             sftp->fstatvfs_request_id, &data, &data_len);
+                             sftp->fstatvfs_request_id, &data, &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
-    } else if (rc) {
+    } else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	if (data_len > 0) {
+	    LIBSSH2_FREE(session, data);
+	}
+	return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+			      "SFTP rename packet too short");
+    }
+    else if (rc) {
         sftp->fstatvfs_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2444,10 +2601,17 @@ static int sftp_statvfs(LIBSSH2_SFTP *sf
     }
 
     rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY,
-                             sftp->statvfs_request_id, &data, &data_len);
+                             sftp->statvfs_request_id, &data, &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
-    } else if (rc) {
+    } else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	if (data_len > 0) {
+	    LIBSSH2_FREE(session, data);
+        }
+	return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+			      "SFTP fstat packet too short");
+    }
+    else if (rc) {
         sftp->statvfs_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2563,10 +2727,18 @@ static int sftp_mkdir(LIBSSH2_SFTP *sftp
     }
 
     rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id,
-                             &data, &data_len);
+                             &data, &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
-    } else if (rc) {
+    }
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP mkdir packet too short");
+    }
+    else if (rc) {
         sftp->mkdir_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2657,10 +2829,18 @@ static int sftp_rmdir(LIBSSH2_SFTP *sftp
     }
 
     rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
-                             sftp->rmdir_request_id, &data, &data_len);
+                             sftp->rmdir_request_id, &data, &data_len, 9);
     if (rc == LIBSSH2_ERROR_EAGAIN) {
         return rc;
-    } else if (rc) {
+    }
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP rmdir packet too short");
+    }
+    else if (rc) {
         sftp->rmdir_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2770,10 +2950,17 @@ static int sftp_stat(LIBSSH2_SFTP *sftp,
     }
 
     rc = sftp_packet_requirev(sftp, 2, stat_responses,
-                              sftp->stat_request_id, &data, &data_len);
-    if (rc == LIBSSH2_ERROR_EAGAIN) {
+                              sftp->stat_request_id, &data, &data_len, 9);
+    if (rc == LIBSSH2_ERROR_EAGAIN)
         return rc;
-    } else if (rc) {
+    else if (rc == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+	if (data_len > 0) {
+	    LIBSSH2_FREE(session, data);
+	}
+	return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+			      "SFTP stat packet too short");
+    }
+    else if (rc) {
         sftp->stat_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
                               "Timeout waiting for status message");
@@ -2796,7 +2983,12 @@ static int sftp_stat(LIBSSH2_SFTP *sftp,
     }
 
     memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-    sftp_bin2attr(attrs, data + 5);
+    if (sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
+        LIBSSH2_FREE(session, data);
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "Attributes too short in SFTP fstat");
+    }
+
     LIBSSH2_FREE(session, data);
 
     return 0;
@@ -2901,9 +3093,16 @@ static int sftp_symlink(LIBSSH2_SFTP *sf
 
     retcode = sftp_packet_requirev(sftp, 2, link_responses,
                                    sftp->symlink_request_id, &data,
-                                   &data_len);
+                                   &data_len, 9);
     if (retcode == LIBSSH2_ERROR_EAGAIN)
         return retcode;
+    else if (retcode == LIBSSH2_ERROR_OUT_OF_BOUNDARY) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP symlink packet too short");
+    }
     else if (retcode) {
         sftp->symlink_state = libssh2_NB_state_idle;
         return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
@@ -2933,6 +3132,14 @@ static int sftp_symlink(LIBSSH2_SFTP *sf
                               "no name entries");
     }
 
+    if (data_len < 13) {
+        if (data_len > 0) {
+            LIBSSH2_FREE(session, data);
+        }
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+                              "SFTP stat packet too short");
+    }
+
     /* this reads a u32 and stores it into a signed 32bit value */
     link_len = _libssh2_ntohu32(data + 9);
     if (link_len < target_len) {
