You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use of the inhsocket option with nullmodem causes DOSBox to crash when it closes or when disabling the serial port #4112
I have checked releases and am using the latest release.
Different version than latest?
No response
What Operating System are you using?
Windows 10
If Other OS, please describe
No response
Relevant hardware info
Intel i7 4790k, 32GB RAM, NVidia 2070 Super
Have you checked that no other similar issue already exists?
I have searched and not found similar issues.
A clear and concise description of what the bug is.
When using the inhsocket option with the serial nullmodem it causes DOSBox to crash when closing regardless of how you close it. If you try and disable the serial port using, for example, "serial1 disabled" the same crash occurs. This occurs when using either a TCP or UDP parent socket. I've even tried duplicating the socket and passing the duplicated socket number and it still crashes.
I noticed this with version 0.80.1 and went on to test 0.81.2 and 0.82.0. This does not occur with DOSBox SVN r4336 (the revision I'm using for the Win9x build of my project) under Win9x or Windows 10.
Aside from this crash, the inhsocket feature works perfectly.
Steps to reproduce the behaviour.
Explain how to reproduce
Create a UDP or TCP socket from a parent program.
Have the parent program spawn a child process to run DOSBox (instructing it to inherit handles) passing the socket number on the command line to dosbox.
inside dosbox setup a serial port to use inhsocket: "serial1 nullmodem inhsocket:1"
type, "exit" or "serial1 disabled" or just close the DOSBox window
No problem at all and thanks for the quick reply on this.
@NicknineTheEagle You care about multiplayer & mode emulation, don't you? (and you're on Windows)
Ha, you bet. I've implemented in my gaming service a means to link DOS games running natively under Win9x that use serial null modem to DOSBox instances over the Internet and the socket inheritance feature of DOSBox allowed for making it very efficient.
I need some clarification on the reproduction steps, ideally with a minimally reproducible program that creates the socket and spawns the child.
I took a look at the code and just in case you weren't aware, the socket parameter it takes is actually a file descriptor. The first thing is does is call getpeername() with the command line socket argument as its first parameter.
I tried to reproduce this myself but it seems you also need to do more than simply create the socket. The socket needs to actually be connected to something or getpeername() returns an error and the connection fails (gracefully in my case, I can't reproduce the crash).
This is my quick and dirty socket + launch DOSBox code but it's Linux specifc. I need to know what exactly you're doing to trigger this.
#include<errno.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<unistd.h>intmain(void)
{
intfd=socket(AF_INET, SOCK_STREAM, 0);
if (fd==-1) {
fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
}
charsock_str[32];
sprintf(sock_str, "%d", fd);
constchar*path="/home/daniel/code/dosbox-staging/build/debug/dosbox";
if (execl(path, path, "--socket", sock_str, (char*)NULL) ==-1) {
fprintf(stderr, "Failed to launch DOSBox: %s\n", strerror(errno));
}
return0;
}
Yes, I know it's a File Descriptor. And yes, the socket has to be connected to something. I stated that the inherited socket functionality is working, but for whatever reason ... very likely something in the null modem destructor is causing DOSBox to crash.
Under Windows the child process that is spawned has to be instructed to inherit handles so the socket number can be passed to DOSBox and it actually work. You would use CreateProcess() to do this.
I can provide a Windows binary I put together to test with when I first was looking into using the inhsocket option, but had tested with DOSBox SVN and didn't discover DOSBox Staging had an issue until I implemented everything in my client software. It's possible this may only be occurring under the Windows builds of DOSBox Staging.
My test program is written in Delphi 10.3 so you likely won't be able to use the source from it.
The source in C/C++ would essentially be the same thing as what you wrote above, but you have to use CreateProcess() to spawn the child process under windows. The issue though is when you close DOSBox or simply try and disable the serial port using something like "serial1 disabled". This is only happening under DOSBox Staging. mainline DOSBox is fine.
But, give me a few minutes and I'll post my test application.
haha ok I don't know Delphi. What are you connecting the socket to though? I think that's the main thing I'm missing. I'm not too sure what the usecase of this feature is.
The normal usage is that the socket is connected to my real relay server which uses UDP.
The test application is setting up a UDP socket which doesn't technically have to "connect" to anything since it's connectionless, but I need to add some input fields to allow you to set a host and port value in since I just had some hard coded values while I was testing and switching between TCP and UDP to see if there was a difference between them. There wasn't.
With UDP it's a easier to test since you don't have to worry about it connecting to anything. The socket opens regardless and DB Staging will present the same crash as when it's running within my real software whereas DB SVN does not in both cases.
Just put the full path to the DOSBox exe in the first edit box and click start. It will create the socket, launch DB passing the socket number and you can enter the command "serial1 nullmodem inhsocket:1" and close DB and it should crash.
The interesting this is that the SVN codebase is exactly the same:
if (getBituSubstring("inhsocket:", &bool_temp, cmd)) {
#ifdef NATIVESOCKETS
if (Netwrapper_GetCapabilities()&NETWRAPPER_TCP_NATIVESOCKET) {
if (bool_temp==1) {
int sock;
if (control->cmdline->FindInt("-socket",sock,true)) {
dtrrespect=false;
transparent=true;
LOG_MSG("Inheritance socket handle: %d",sock);
if (!ClientConnect(new TCPClientSocket(sock)))
return;
} else {
LOG_MSG("Serial%d: -socket parameter missing.",COMNUMBER);
return;
}
}
} else {
LOG_MSG("Serial%d: socket inheritance not supported on this platform.",
COMNUMBER);
return;
}
#else
LOG_MSG("Serial%d: socket inheritance not available.", COMNUMBER);
#endif
But I don't think that matters since the socket code works regardless of what it was initialized as. Since it's just reading and writing to the socket. And like I said, it works just fine in usage. It just appears something is not being cleaned up properly when destroying the null modem object. Also, keep in mind that DB Staging also has the "sock" option you can also pass to the nullmodem which tells it if it's using TCP or UDP and I tried using that option and it doesn't affect the outcome.
Activity
jpetriejr commentedon Dec 18, 2024
Happens under 32 and 64-bit builds.
johnnovak commentedon Dec 19, 2024
Thanks for the detailed report @jpetriejr and for the regression testing. Very well written 💯
I'm glad to hear this is not a show stopper just a minor annoyance at exit.
@NicknineTheEagle You care about multiplayer & mode emulation, don't you? (and you're on Windows)
jpetriejr commentedon Dec 19, 2024
No problem at all and thanks for the quick reply on this.
Ha, you bet. I've implemented in my gaming service a means to link DOS games running natively under Win9x that use serial null modem to DOSBox instances over the Internet and the socket inheritance feature of DOSBox allowed for making it very efficient.
NicknineTheEagle commentedon Dec 19, 2024
This looks like a null-modem option, I don't believe I've ever touched that. ^_^' I suppose you would need to look into CNullModem destructor.
johnnovak commentedon Dec 19, 2024
Yeah. I'll leave it to someone into this stuff, I don't believe it's widely used and I've never touched it. Plus it's just a shutdown annoyance.
weirddan455 commentedon Dec 20, 2024
I need some clarification on the reproduction steps, ideally with a minimally reproducible program that creates the socket and spawns the child.
I took a look at the code and just in case you weren't aware, the
socketparameter it takes is actually a file descriptor. The first thing is does is callgetpeername()with the command linesocketargument as its first parameter.I tried to reproduce this myself but it seems you also need to do more than simply create the socket. The socket needs to actually be connected to something or
getpeername()returns an error and the connection fails (gracefully in my case, I can't reproduce the crash).This is my quick and dirty socket + launch DOSBox code but it's Linux specifc. I need to know what exactly you're doing to trigger this.
jpetriejr commentedon Dec 20, 2024
Yes, I know it's a File Descriptor. And yes, the socket has to be connected to something. I stated that the inherited socket functionality is working, but for whatever reason ... very likely something in the null modem destructor is causing DOSBox to crash.
Under Windows the child process that is spawned has to be instructed to inherit handles so the socket number can be passed to DOSBox and it actually work. You would use CreateProcess() to do this.
I can provide a Windows binary I put together to test with when I first was looking into using the inhsocket option, but had tested with DOSBox SVN and didn't discover DOSBox Staging had an issue until I implemented everything in my client software. It's possible this may only be occurring under the Windows builds of DOSBox Staging.
weirddan455 commentedon Dec 20, 2024
I can test it under Windows if you give me something to test with. I'd prefer source code over a binary so I can fully understand what is happening.
jpetriejr commentedon Dec 20, 2024
My test program is written in Delphi 10.3 so you likely won't be able to use the source from it.
The source in C/C++ would essentially be the same thing as what you wrote above, but you have to use CreateProcess() to spawn the child process under windows. The issue though is when you close DOSBox or simply try and disable the serial port using something like "serial1 disabled". This is only happening under DOSBox Staging. mainline DOSBox is fine.
But, give me a few minutes and I'll post my test application.
weirddan455 commentedon Dec 20, 2024
haha ok I don't know Delphi. What are you connecting the socket to though? I think that's the main thing I'm missing. I'm not too sure what the usecase of this feature is.
jpetriejr commentedon Dec 20, 2024
The normal usage is that the socket is connected to my real relay server which uses UDP.
The test application is setting up a UDP socket which doesn't technically have to "connect" to anything since it's connectionless, but I need to add some input fields to allow you to set a host and port value in since I just had some hard coded values while I was testing and switching between TCP and UDP to see if there was a difference between them. There wasn't.
With UDP it's a easier to test since you don't have to worry about it connecting to anything. The socket opens regardless and DB Staging will present the same crash as when it's running within my real software whereas DB SVN does not in both cases.
jpetriejr commentedon Dec 20, 2024
Test.zip

jpetriejr commentedon Dec 20, 2024
Just put the full path to the DOSBox exe in the first edit box and click start. It will create the socket, launch DB passing the socket number and you can enter the command "serial1 nullmodem inhsocket:1" and close DB and it should crash.
jpetriejr commentedon Dec 20, 2024
Went ahead and ran it using DB Staging just to show the crash when attempting to exit.
weirddan455 commentedon Dec 20, 2024
I only see code in Staging for TCP when using
inhsocketso I don't think that's a good test.dosbox-staging/src/hardware/serialport/nullmodem.cpp
Lines 88 to 116 in ad84f6f
So if you pass it a UDP socket, I'm not surprised if things blow up 😄
jpetriejr commentedon Dec 20, 2024
The interesting this is that the SVN codebase is exactly the same:
But I don't think that matters since the socket code works regardless of what it was initialized as. Since it's just reading and writing to the socket. And like I said, it works just fine in usage. It just appears something is not being cleaned up properly when destroying the null modem object. Also, keep in mind that DB Staging also has the "sock" option you can also pass to the nullmodem which tells it if it's using TCP or UDP and I tried using that option and it doesn't affect the outcome.
weirddan455 commentedon Dec 20, 2024
Actually this looks like a use after free/double free bug. In the constructor for
TCPClientSocket:dosbox-staging/src/hardware/serialport/misc_util.cpp
Lines 551 to 552 in ad84f6f
Then in the destructor:
dosbox-staging/src/hardware/serialport/misc_util.cpp
Lines 622 to 635 in ad84f6f
I think
mysock == nativetcpstruct. Can you try removing thatdelete nativetcpstruct;and see if it fixes it?jpetriejr commentedon Dec 20, 2024
Man, I don't have the DOSBox Staging source downloaded or setup to debug or fix. But here is the constructor/destructor from DB SVN:
weirddan455 commentedon Dec 20, 2024
They have an
elseafter they deletenativetcpstructso it doesn't double free. I'm betting that's the bug. I'll test on Windows later to confirm.jpetriejr commentedon Dec 20, 2024
Yep, good catch!
And thanks for taking the time to look into this. I greatly appreciate it.
weirddan455 commentedon Dec 20, 2024
Thanks for the test program @jpetriejr
That reproduced the bug for me nicely on Windows and I put a PR in with the fix.
jpetriejr commentedon Dec 20, 2024
Beautiful! Thank you sir.