diff --git a/frankenphp.c b/frankenphp.c index 6bc3e0c1da..ee5c869c62 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -954,16 +954,12 @@ static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) { return SAPI_HEADER_SENT_SUCCESSFULLY; } - int status; - - if (SG(sapi_headers).http_status_line) { - status = atoi((SG(sapi_headers).http_status_line) + 9); - } else { - status = SG(sapi_headers).http_response_code; - - if (!status) { - status = 200; - } + /* Use the response code PHP already parsed; reparsing http_status_line with + * a fixed +9 offset read out of bounds for lines shorter than 9 bytes, e.g. + * header("HTTP/"). */ + int status = SG(sapi_headers).http_response_code; + if (!status) { + status = 200; } bool success = go_write_headers(frankenphp_thread_index(), status, diff --git a/statusline_test.go b/statusline_test.go new file mode 100644 index 0000000000..5843ab84f4 --- /dev/null +++ b/statusline_test.go @@ -0,0 +1,45 @@ +package frankenphp_test + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/dunglas/frankenphp" + "github.com/stretchr/testify/require" +) + +func serveStatusScript(t *testing.T, script string) *http.Response { + t.Helper() + + cwd, _ := os.Getwd() + req := httptest.NewRequest(http.MethodGet, "http://example.com/"+script, nil) + fr, err := frankenphp.NewRequestWithContext(req, + frankenphp.WithRequestDocumentRoot(cwd+"/testdata/", false)) + require.NoError(t, err) + + w := httptest.NewRecorder() + require.NoError(t, frankenphp.ServeHTTP(w, fr)) + + return w.Result() +} + +// A status line shorter than 9 bytes (e.g. header("HTTP/")) once made +// frankenphp_send_headers read out of bounds via atoi(http_status_line + 9). +// Run under -asan to catch a regression. +func TestShortStatusLineDoesNotOverflow(t *testing.T) { + require.NoError(t, frankenphp.Init()) + defer frankenphp.Shutdown() + + resp := serveStatusScript(t, "short-status-line.php") + require.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestCustomStatusLineIsHonored(t *testing.T) { + require.NoError(t, frankenphp.Init()) + defer frankenphp.Shutdown() + + resp := serveStatusScript(t, "custom-status-line.php") + require.Equal(t, http.StatusTeapot, resp.StatusCode) +} diff --git a/testdata/custom-status-line.php b/testdata/custom-status-line.php new file mode 100644 index 0000000000..0a135d7a3f --- /dev/null +++ b/testdata/custom-status-line.php @@ -0,0 +1,4 @@ +