41a6c3
diff --git a/server/protocol.c b/server/protocol.c
41a6c3
index 9e23325..8428129 100644
41a6c3
--- a/server/protocol.c
41a6c3
+++ b/server/protocol.c
41a6c3
@@ -222,6 +222,12 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
     int fold = flags & AP_GETLINE_FOLD;
41a6c3
     int crlf = flags & AP_GETLINE_CRLF;
41a6c3
 
41a6c3
+    if (!n) {
41a6c3
+        /* Needs room for NUL byte at least */
41a6c3
+        *read = 0;
41a6c3
+        return APR_BADARG;
41a6c3
+    }
41a6c3
+
41a6c3
     /*
41a6c3
      * Initialize last_char as otherwise a random value will be compared
41a6c3
      * against APR_ASCII_LF at the end of the loop if bb only contains
41a6c3
@@ -235,14 +241,15 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
         rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
41a6c3
                             APR_BLOCK_READ, 0);
41a6c3
         if (rv != APR_SUCCESS) {
41a6c3
-            return rv;
41a6c3
+            goto cleanup;
41a6c3
         }
41a6c3
 
41a6c3
         /* Something horribly wrong happened.  Someone didn't block! 
41a6c3
          * (this also happens at the end of each keepalive connection)
41a6c3
          */
41a6c3
         if (APR_BRIGADE_EMPTY(bb)) {
41a6c3
-            return APR_EGENERAL;
41a6c3
+            rv = APR_EGENERAL;
41a6c3
+            goto cleanup;
41a6c3
         }
41a6c3
 
41a6c3
         for (e = APR_BRIGADE_FIRST(bb);
41a6c3
@@ -260,7 +267,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
 
41a6c3
             rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
41a6c3
             if (rv != APR_SUCCESS) {
41a6c3
-                return rv;
41a6c3
+                goto cleanup;
41a6c3
             }
41a6c3
 
41a6c3
             if (len == 0) {
41a6c3
@@ -273,17 +280,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
 
41a6c3
             /* Would this overrun our buffer?  If so, we'll die. */
41a6c3
             if (n < bytes_handled + len) {
41a6c3
-                *read = bytes_handled;
41a6c3
-                if (*s) {
41a6c3
-                    /* ensure this string is NUL terminated */
41a6c3
-                    if (bytes_handled > 0) {
41a6c3
-                        (*s)[bytes_handled-1] = '\0';
41a6c3
-                    }
41a6c3
-                    else {
41a6c3
-                        (*s)[0] = '\0';
41a6c3
-                    }
41a6c3
-                }
41a6c3
-                return APR_ENOSPC;
41a6c3
+                rv = APR_ENOSPC;
41a6c3
+                goto cleanup;
41a6c3
             }
41a6c3
 
41a6c3
             /* Do we have to handle the allocation ourselves? */
41a6c3
@@ -291,7 +289,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
                 /* We'll assume the common case where one bucket is enough. */
41a6c3
                 if (!*s) {
41a6c3
                     current_alloc = len;
41a6c3
-                    *s = apr_palloc(r->pool, current_alloc);
41a6c3
+                    *s = apr_palloc(r->pool, current_alloc + 1);
41a6c3
                 }
41a6c3
                 else if (bytes_handled + len > current_alloc) {
41a6c3
                     /* Increase the buffer size */
41a6c3
@@ -302,7 +300,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
                         new_size = (bytes_handled + len) * 2;
41a6c3
                     }
41a6c3
 
41a6c3
-                    new_buffer = apr_palloc(r->pool, new_size);
41a6c3
+                    new_buffer = apr_palloc(r->pool, new_size + 1);
41a6c3
 
41a6c3
                     /* Copy what we already had. */
41a6c3
                     memcpy(new_buffer, *s, bytes_handled);
41a6c3
@@ -326,19 +324,15 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
         }
41a6c3
     }
41a6c3
 
41a6c3
-    if (crlf && (last_char <= *s || last_char[-1] != APR_ASCII_CR)) {
41a6c3
-        *last_char = '\0';
41a6c3
-        bytes_handled = last_char - *s;
41a6c3
-        *read = bytes_handled;
41a6c3
-        return APR_EINVAL;
41a6c3
-    }
41a6c3
-
41a6c3
-    /* Now NUL-terminate the string at the end of the line;
41a6c3
+    /* Now terminate the string at the end of the line;
41a6c3
      * if the last-but-one character is a CR, terminate there */
41a6c3
     if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
41a6c3
         last_char--;
41a6c3
     }
41a6c3
-    *last_char = '\0';
41a6c3
+    else if (crlf) {
41a6c3
+        rv = APR_EINVAL;
41a6c3
+        goto cleanup;
41a6c3
+    }
41a6c3
     bytes_handled = last_char - *s;
41a6c3
 
41a6c3
     /* If we're folding, we have more work to do.
41a6c3
@@ -358,7 +352,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
             rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
41a6c3
                                 APR_BLOCK_READ, 1);
41a6c3
             if (rv != APR_SUCCESS) {
41a6c3
-                return rv;
41a6c3
+                goto cleanup;
41a6c3
             }
41a6c3
 
41a6c3
             if (APR_BRIGADE_EMPTY(bb)) {
41a6c3
@@ -375,7 +369,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
             rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
41a6c3
             if (rv != APR_SUCCESS) {
41a6c3
                 apr_brigade_cleanup(bb);
41a6c3
-                return rv;
41a6c3
+                goto cleanup;
41a6c3
             }
41a6c3
 
41a6c3
             /* Found one, so call ourselves again to get the next line.
41a6c3
@@ -392,10 +386,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
             if (c == APR_ASCII_BLANK || c == APR_ASCII_TAB) {
41a6c3
                 /* Do we have enough space? We may be full now. */
41a6c3
                 if (bytes_handled >= n) {
41a6c3
-                    *read = n;
41a6c3
-                    /* ensure this string is terminated */
41a6c3
-                    (*s)[n-1] = '\0';
41a6c3
-                    return APR_ENOSPC;
41a6c3
+                    rv = APR_ENOSPC;
41a6c3
+                    goto cleanup;
41a6c3
                 }
41a6c3
                 else {
41a6c3
                     apr_size_t next_size, next_len;
41a6c3
@@ -408,7 +400,6 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
                         tmp = NULL;
41a6c3
                     }
41a6c3
                     else {
41a6c3
-                        /* We're null terminated. */
41a6c3
                         tmp = last_char;
41a6c3
                     }
41a6c3
 
41a6c3
@@ -417,7 +408,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
                     rv = ap_rgetline_core(&tmp, next_size,
41a6c3
                                           &next_len, r, 0, bb);
41a6c3
                     if (rv != APR_SUCCESS) {
41a6c3
-                        return rv;
41a6c3
+                        goto cleanup;
41a6c3
                     }
41a6c3
 
41a6c3
                     if (do_alloc && next_len > 0) {
41a6c3
@@ -431,7 +422,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
                         memcpy(new_buffer, *s, bytes_handled);
41a6c3
 
41a6c3
                         /* copy the new line, including the trailing null */
41a6c3
-                        memcpy(new_buffer + bytes_handled, tmp, next_len + 1);
41a6c3
+                        memcpy(new_buffer + bytes_handled, tmp, next_len);
41a6c3
                         *s = new_buffer;
41a6c3
                     }
41a6c3
 
41a6c3
@@ -444,8 +435,21 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
41a6c3
             }
41a6c3
         }
41a6c3
     }
41a6c3
+
41a6c3
+cleanup:
41a6c3
+    if (bytes_handled >= n) {
41a6c3
+        bytes_handled = n - 1;
41a6c3
+    }
41a6c3
+    if (*s) {
41a6c3
+        /* ensure the string is NUL terminated */
41a6c3
+        (*s)[bytes_handled] = '\0';
41a6c3
+    }
41a6c3
     *read = bytes_handled;
41a6c3
 
41a6c3
+    if (rv != APR_SUCCESS) {
41a6c3
+        return rv;
41a6c3
+    }
41a6c3
+
41a6c3
     /* PR#43039: We shouldn't accept NULL bytes within the line */
41a6c3
     if (strlen(*s) < bytes_handled) {
41a6c3
         return APR_EINVAL;
41a6c3
@@ -484,6 +488,11 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
41a6c3
     apr_size_t len;
41a6c3
     apr_bucket_brigade *tmp_bb;
41a6c3
 
41a6c3
+    if (n < 1) {
41a6c3
+        /* Can't work since we always NUL terminate */
41a6c3
+        return -1;
41a6c3
+    }
41a6c3
+
41a6c3
     tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
41a6c3
     rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb);
41a6c3
     apr_brigade_destroy(tmp_bb);