/* Provenance: openssl/openssl ssl3_read_n (fixed). * repo: openssl/openssl * commit: 2d05959073c4bf8803401668b9df85931a08e020 * parent: b3f0eb0a295f58f16ba43ba99dad70d4ee5c437d * commit_url: https://github.com/openssl/openssl/commit/2d05959073c4bf8803401668b9df85931a08e020 * cve: CVE-2024-4741 * license: Apache-2.0 * function: ssl3_read_n * relpath: ssl/record/rec_layer_s3.c */ int ssl3_read_n(SSL *s, size_t n, size_t max, int extend, int clearold, size_t *readbytes) { /* * If extend == 0, obtain new n-byte packet; if extend == 1, increase * packet by another n bytes. The packet will be in the sub-array of * s->rlayer.rbuf.buf specified by s->rlayer.packet and * s->rlayer.packet_length. (If s->rlayer.read_ahead is set, 'max' bytes may * be stored in rbuf [plus s->rlayer.packet_length bytes if extend == 1].) * if clearold == 1, move the packet to the start of the buffer; if * clearold == 0 then leave any old packets where they were */ size_t len, left, align = 0; unsigned char *pkt; SSL3_BUFFER *rb; if (n == 0) return 0; rb = &s->rlayer.rbuf; if (rb->buf == NULL) if (!ssl3_setup_read_buffer(s)) { /* SSLfatal() already called */ return -1; } left = rb->left; #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0 align = (size_t)rb->buf + SSL3_RT_HEADER_LENGTH; align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD); #endif if (!extend) { /* start with empty packet ... */ if (left == 0) rb->offset = align; s->rlayer.packet = rb->buf + rb->offset; s->rlayer.packet_length = 0; /* ... now we can act as if 'extend' was set */ } if (!ossl_assert(s->rlayer.packet != NULL)) { /* does not happen */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return -1; } len = s->rlayer.packet_length; pkt = rb->buf + align; /* * Move any available bytes to front of buffer: 'len' bytes already * pointed to by 'packet', 'left' extra ones at the end */ if (s->rlayer.packet != pkt && clearold == 1) { memmove(pkt, s->rlayer.packet, len + left); s->rlayer.packet = pkt; rb->offset = len + align; } /* * For DTLS/UDP reads should not span multiple packets because the read * operation returns the whole packet at once (as long as it fits into * the buffer). */ if (SSL_IS_DTLS(s)) { if (left == 0 && extend) return 0; if (left > 0 && n > left) n = left; } /* if there is enough in the buffer from a previous read, take some */ if (left >= n) { s->rlayer.packet_length += n; rb->left = left - n; rb->offset += n; *readbytes = n; return 1; } /* else we need to read more data */ if (n > rb->len - rb->offset) { /* does not happen */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return -1; } /* * Ktls always reads full records. * Also, we always act like read_ahead is set for DTLS. */ if (!BIO_get_ktls_recv(s->rbio) && !s->rlayer.read_ahead && !SSL_IS_DTLS(s)) { /* ignore max parameter */ max = n; } else { if (max < n) max = n; if (max > rb->len - rb->offset) max = rb->len - rb->offset; } while (left < n) { size_t bioread = 0; int ret; /* * Now we have len+left bytes at the front of s->s3.rbuf.buf and * need to read in more until we have len+n (up to len+max if * possible) */ clear_sys_error(); if (s->rbio != NULL) { s->rwstate = SSL_READING; ret = BIO_read(s->rbio, pkt + len + left, max - left); if (ret >= 0) bioread = ret; if (ret <= 0 && !BIO_should_retry(s->rbio) && BIO_eof(s->rbio)) { if (s->options & SSL_OP_IGNORE_UNEXPECTED_EOF) { SSL_set_shutdown(s, SSL_RECEIVED_SHUTDOWN); s->s3.warn_alert = SSL_AD_CLOSE_NOTIFY; } else { /* * This reason code is part of the API and may be used by * applications for control flow decisions. */ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_UNEXPECTED_EOF_WHILE_READING); } } } else { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_READ_BIO_NOT_SET); ret = -1; } if (ret <= 0) { rb->left = left; if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s)) if (len + left == 0) ssl3_release_read_buffer(s); return ret; } left += bioread; /* * reads should *never* span multiple packets for DTLS because the * underlying transport protocol is message oriented as opposed to * byte oriented as in the TLS case. */ if (SSL_IS_DTLS(s)) { if (n > left) n = left; /* makes the while condition false */ } } /* done reading, now the book-keeping */ rb->offset += n; rb->left = left - n; s->rlayer.packet_length += n; s->rwstate = SSL_NOTHING; *readbytes = n; return 1; }