Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 58 additions & 30 deletions scp.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,19 @@
#include ".git-commit-id.h"
#endif

#if !defined(_WIN32) && !defined(_WIN64)
/* fsync() */
#include <unistd.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <io.h>

/* fsync() wrapper for Windows. */
static inline int fsync(int fd)
{
return _commit(fd);
}
#endif

#ifndef MAX
#define MAX(a,b) (((a) >= (b)) ? (a) : (b))
#endif
Expand Down Expand Up @@ -13611,14 +13624,49 @@ static void _debug_fwrite_all (const char *buf, size_t len, FILE *f)
{
size_t len_written;

while (len > 0) {
errno = 0;
len_written = fwrite (buf, 1, len, f);
len -= len_written;
buf += len_written;
if (errno == EAGAIN) /* Non blocking file descriptor buffer full? */
sim_os_ms_sleep(10);/* wait a bit to retry */
}
/* NOTE: There be a dragon here. This is UNSAFE in a multithreaded SIMH. This
* race could still exist if sim_set_deboff() is called prematurely before all
* threads are quiescent (needs confirmation.)
*
* _sim_debug_flush() calls sim_set_deboff(0, NULL), which NULLs sim_deb (aka f).
* This causes a race on sim_deb between threads -- a thread can read a NULL sim_deb
* and call _debug_fwrite_all() with f == NULL.
*
* f == NULL: fwrite() returns 0 on Linux and Windows. len never gets decremented
* and an infinite loop ensues (cue "Forever NULL" sung to the tune of Alphaville's
* "Forever Young".)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply treat length written == 0 as an error? But in my testing, a NULL file pointer causes a segfault (on recent Linux as well as on Mac OS).

Copy link
Contributor Author

@bscottm bscottm Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pkoning2: I left the "dragon" note as more of a "Don't Do This" warning. I added extra checks deal with the f == NULL,len_written == 0 and errno != EAGAIN cases. In each case, SIMH outputs a diagnostic message (the "why") and the buffer's contents that weren't written to the debug output.

*/
if (f != NULL && len > 0) {
while (len > 0) {
errno = 0;
len_written = fwrite (buf, 1, len, f);
len -= len_written;
buf += len_written;
if (errno == EAGAIN) { /* Non blocking file descriptor buffer full? */
sim_os_ms_sleep(10);/* wait a bit to retry */
}
else if (len_written == 0 || errno > 0) {
/* Deal with the dragon, although unlikely to ever get emitted. */
if (len_written == 0)
fputs("Debug output truncated, unable to write output.\n", stderr);
else
perror("Debug output truncated/not written due to error");
fputs("Message was:\n", stderr);
fwrite(buf, sizeof(char), len, stderr);
fputc('\n', stderr);
fflush(stderr);
break;
}
}
} else {
/* Deal with the dragon, although, realisitically, this message should
* be rarely emitted. */
fputs("Debug output truncated/not written, FILE is NULL.\n", stderr);
fputs("Message was:\n", stderr);
fwrite(buf, sizeof(char), len, stderr);
fputc('\n', stderr);
fflush(stderr);
}
}

static void _debug_fwrite (const char *buf, size_t len)
Expand Down Expand Up @@ -13755,33 +13803,13 @@ _sim_debug_write_flush (buf, len, FALSE);

static t_stat _sim_debug_flush (void)
{
int32 saved_quiet = sim_quiet;
int32 saved_sim_switches = sim_switches;
int32 saved_deb_switches = sim_deb_switches;
struct timespec saved_deb_basetime = sim_deb_basetime;
char saved_debug_filename[CBUFSIZE];

if (sim_deb == NULL) /* no debug? */
return SCPE_OK;

_sim_debug_write_flush ("", 0, TRUE);
fflush (sim_deb);
fsync(fileno(sim_deb));

if (sim_deb == sim_log) { /* debug is log */
fflush (sim_deb); /* fflush is the best we can do */
return SCPE_OK;
}

if (!(saved_deb_switches & SWMASK ('B'))) {
strcpy (saved_debug_filename, sim_logfile_name (sim_deb, sim_deb_ref));

sim_quiet = 1;
sim_set_deboff (0, NULL);
sim_switches = saved_deb_switches;
sim_set_debon (0, saved_debug_filename);
sim_deb_basetime = saved_deb_basetime;
sim_switches = saved_sim_switches;
sim_quiet = saved_quiet;
}
return SCPE_OK;
}

Expand Down
Loading