The "FD" is not "executed" as such. It is used to send data to the open socket from a CGI app that is run via an exec() call. The O_CLOEXEC  however causes the FD to be closed on the exec(), and the dup2() that happens in that exec'd CGI handling gets reissued with the same number, meaning the eventual write to the socket from the CGI "app" gets sent to something that can't handle it and it all fails.

The change to add O_CLOEXEC was in a specific PR, 8 months ago along with a discussion that (in my opinion) should have resulted in it NOT being added to thttpd if thttpd had been checked a little more thoroughly perhaps. It was an change to multiple files.

nuttx-apps issue: https://github.com/apache/nuttx-apps/issues/3111

original PR: https://github.com/apache/nuttx-apps/pull/2595

Simply reverting that change fixes it. The only thing needed.

On 01/07/2025 19:00, Alan C. Assis wrote:
Hi Tim,

You are right, it doesn't execute, but some subprocess (like a CGI) could
try to execute.

This comment there shed some light about it:

"I wouldn't describe O_CLOEXEC as there principally for privilege
escalation / security reasons -- it's also very,
very common to have non-security bugs happen (frequently of the
indefinite-blocking variety) if a FD is left
open beyond when it's intended to be closed because a subprocess still has
it."

So, why does removing SOCK_CLOEXEC make http work? If the fd is not
executed, the socket shouldn't be closed, right?

And why was it working in the past? Which modification broke this?
Maybe understanding it is important to have the right fix (maybe removing
it is acting as a band-aid).

Wengzhe, could you please help us to understand this network issue?

BR,

Alan

On Tue, Jul 1, 2025 at 12:28 PM Tim Hardisty <timhardist...@gmail.com>
wrote:

But that's the point - thttp *does* call exec() so the open socket file
descriptor gets closed when it is still needed by the exec'd application.

If there's another way of doing this I'm listening!

On 01/07/2025 16:13, Alan C. Assis wrote:
Hi Tim,

Nice finding!

Now we need to understand why this worked in the past and now it doesn't.

Also, what are the implications of removing SOCK_CLOEXEC? A few pointers
here:

https://stackoverflow.com/questions/22304631/what-is-the-purpose-to-set-sock-cloexec-flag-with-accept4-same-as-o-cloexec
BR,

Alan

On Tue, Jul 1, 2025 at 11:27 AM Tim Hardisty <timhardist...@gmail.com>
wrote:

The error was, indeed, the socket being opened with the SOCK_CLOEXEC
flasg set.

PR to follow.

On 28/06/2025 16:16, Tim Hardisty wrote:
Actually - it might be a change last year. The socket is now opened
like this and I assume CLOEXEC will mess up the operation of the
executed CGI app (will investigate on Monday; not sure what socket
mode it needs to be):

hc->conn_fd = accept4(listen_fd, (struct sockaddr *)&sa, &sz,
SOCK_CLOEXEC);

On 28/06/2025 13:22, Alan C. Assis wrote:
Hi Tim,

Yes, I think send() is the preferred form to work with sockets
because you
can have fine control, i.e. passing flags at forth argument
(MSG_DONTWAIT,
etc).

If you suspect that the bug was caused by some recent modification,
try to
find a supported board that was used to test thttpd in the past and
test an
old NuttX release with it.
This is the approach I use to double check if something is broken in
the
mainline.

BR,

Alan

On Fri, Jun 27, 2025 at 3:39 PM Tim Hardisty <timhardist...@gmail.com
wrote:

Is it as "simple" as thttpd should do:

nwritten= send(sock_fd, buffer, totalbytesread, 0);

rather than the generic:

nwritten= write(sock_fd, buffer, nbytes);

On 27/06/2025 18:40, Tim Hardisty wrote:
Trying to get thttpd's CGI handling working and have found that the
dup(2) calls of stdin and stdout return a file descriptor that's
already been allocated to the NET socket (via thttpd I think).

That isn't right is it?

I am not sure if it's a side effect of something that thttpd does
(that might have been OK in the past but is now not right) or a
NuttX
bug, of a missing Kconfig setting that relates to this.

The result is that the ultimate copying of buffered html that should
be written via the socket FD gets rejected as the FD doesn't have WR
access (and is now the wrong FD anyway!).

Perhaps there's been a change in the way NuttX deals with all of
this
that didn't get sorted in thttpd?

Reply via email to