From: bones_was_here <bones_was_here@xonotic.au>
Date: Tue, 23 Jul 2024 21:32:50 +0000 (+1000)
Subject: com: fix loop/crash in dpvsnprintf() error handling
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=287ee9b875749f2e251c37afd25f7daa4c45bd35;p=xonotic%2Fdarkplaces.git

com: fix loop/crash in dpvsnprintf() error handling

Somehow I didn't notice Sys_Printf() calls dpvsnprintf(), see
21fa9010ae6de8792f36cca08cb77aaa95d4b928.

Also fixes a minor bug where Sys_Print() could output more bytes than it
should, introduced in 01642af37128bffc1accbf2e92cb2428ea570720.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
---

diff --git a/common.c b/common.c
index c5c33cf1..fb13b16c 100644
--- a/common.c
+++ b/common.c
@@ -1018,12 +1018,27 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
 #endif
 	if (result < 0 || (size_t)result >= buffersize)
 	{
-		buffer[buffersize - 1] = '\0';
-		// we could be inside Con_Printf
+		// _vsnprintf_s returns -1 on error and on truncation (indistinguishable),
+		// vsnprintf returns negative on error and the desired strlen on truncation.
+		buffer[buffersize - 1] = '\0'; // should be unnecessary, but just in case
+		// Basic stdout only: we could be inside Con_Printf, Sys_Printf calls dpvsnprintf,
+		// Windows console doesn't support colours.
 		if (result < 0)
-			Sys_Printf("dpvsnprintf: output error, buffer size %lu\n", (unsigned long)buffersize);
+			Sys_Print("dpvsnprintf: output error!\n", 27);
 		else
-			Sys_Printf("dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
+		{
+			char msg[MAX_INPUTLINE];
+#if _MSC_VER >= 1400
+			result = _snprintf_s(msg, sizeof(msg), _TRUNCATE, "dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
+#else
+			result = snprintf(msg, sizeof(msg), "dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
+#endif
+			if (result > 0)
+			{
+				msg[sizeof(msg) - 1] = '\n'; // may have been lost in truncation
+				Sys_Print(msg, min((size_t)result, sizeof(msg)));
+			}
+		}
 		return -1;
 	}
 
diff --git a/sys_shared.c b/sys_shared.c
index 46cec642..b955e827 100644
--- a/sys_shared.c
+++ b/sys_shared.c
@@ -629,12 +629,13 @@ void Sys_Print(const char *text, size_t textlen)
   #else
     #define write _write
   #endif
-		while(*text)
+		while(*text && textlen)
 		{
 			fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
 			if(written <= 0)
 				break; // sorry, I cannot do anything about this error - without an output
 			text += written;
+			textlen -= written;
 		}
   #ifndef WIN32
 		if (sys_stdout_blocks.integer)