hatari: 1 CWE-407 defect, MOAD 0002-0005 CLEAN

This commit is contained in:
russell@unturf.com 2026-03-31 17:24:29 -04:00
parent 64dee0eb9c
commit 2611f389b6
2 changed files with 300 additions and 0 deletions

View file

@ -0,0 +1,101 @@
# UNDF: UNDF-2026-000001040
--- a/src/ikbd.c
+++ b/src/ikbd.c
@@ -218,6 +218,28 @@ static const struct {
void (*pCallFunction)(void);
} KeyboardCommands[] =
{
+ /* Dispatch table built at init from KeyboardCommands[] for O(1) lookup. */
+ /* See IKBD_InitDispatch() and IKBD_RunKeyboardCommand(). */
/* Known messages, counts include command byte */
{ 0x80,2, IKBD_Cmd_Reset },
{ 0x07,2, IKBD_Cmd_MouseAction },
@@ -258,6 +280,33 @@ static const struct {
{ 0xFF,0, NULL } /* Term */
};
+/* Dispatch table: indexed by command byte, holds index into KeyboardCommands[]
+ * or -1 for unknown commands. Built once by IKBD_InitDispatch(). */
+static int IKBDCmdIndex[256];
+
+/**
+ * Build the O(1) command-dispatch index from KeyboardCommands[].
+ * Called once during IKBD initialisation.
+ */
+static void IKBD_InitDispatch(void)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ IKBDCmdIndex[i] = -1;
+ for (i = 0; KeyboardCommands[i].Command != 0xFF; i++)
+ IKBDCmdIndex[KeyboardCommands[i].Command] = i;
+}
+
/*----------------------------------------------------------------------*/
/* Variables/defines/functions used to transfer data between the */
@@ -830,6 +879,7 @@ void IKBD_Reset(bool bCold)
/* Reset */
memset(&Keyboard, 0, sizeof(Keyboard));
+ IKBD_InitDispatch();
+
/* IKBD gets reset whenever the computer does */
IKBD_ResetKeyboardProcessor();
@@ -1854,22 +1904,22 @@ static void IKBD_RunKeyboardCommand(uint8_t aciabyte)
{
- int i=0;
/* Write into our keyboard input buffer if it's not full yet */
if ( Keyboard.nBytesInInputBuffer < SIZE_KEYBOARDINPUT_BUFFER )
Keyboard.InputBuffer[Keyboard.nBytesInInputBuffer++] = aciabyte;
- /* Now check bytes to see if we have a valid/in-valid command string set */
- while (KeyboardCommands[i].Command!=0xff)
- {
- /* Found command? */
- if (KeyboardCommands[i].Command==Keyboard.InputBuffer[0])
- {
- /* If the command is complete (with its possible parameters) we can execute it */
- /* Else, we wait for the next bytes until the command is complete */
- if (KeyboardCommands[i].NumParameters==Keyboard.nBytesInInputBuffer)
- {
- /* Any new valid command will unpause the output (if command 0x13 was used) */
- Keyboard.PauseOutput = false;
-
- CALL_VAR(KeyboardCommands[i].pCallFunction);
- Keyboard.nBytesInInputBuffer = 0; /* Clear input buffer after processing a command */
- }
-
- return;
- }
-
- i++;
- }
-
- /* Command not known, reset buffer(IKBD assumes a NOP) */
- Keyboard.nBytesInInputBuffer = 0;
+ /* O(1) dispatch: look up command byte directly, no linear scan needed. */
+ {
+ int idx = IKBDCmdIndex[(unsigned char)Keyboard.InputBuffer[0]];
+ if (idx >= 0)
+ {
+ /* If the command is complete (with its possible parameters) we can execute it */
+ /* Else, we wait for the next bytes until the command is complete */
+ if (KeyboardCommands[idx].NumParameters==Keyboard.nBytesInInputBuffer)
+ {
+ /* Any new valid command will unpause the output (if command 0x13 was used) */
+ Keyboard.PauseOutput = false;
+
+ CALL_VAR(KeyboardCommands[idx].pCallFunction);
+ Keyboard.nBytesInInputBuffer = 0; /* Clear input buffer after processing a command */
+ }
+
+ return;
+ }
+
+ /* Command not known, reset buffer (IKBD assumes a NOP) */
+ Keyboard.nBytesInInputBuffer = 0;
+ }
}

View file

@ -0,0 +1,199 @@
/*
* test_hatari_0001.c
*
* Unit test for hatari-0001: IKBD_RunKeyboardCommand O(C) linear scan
* replaced with O(1) dispatch table.
*
* Tests that the dispatch table correctly maps all known command bytes
* and returns -1 for unknown bytes, verifying O(1) correctness.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
/* Minimal reproduction of the KeyboardCommands table and dispatch logic. */
typedef struct {
uint8_t Command;
uint8_t NumParameters;
void (*pCallFunction)(void);
} KeyboardCommand;
/* Representative subset of IKBD commands from Hatari's ikbd.c */
static const KeyboardCommand KeyboardCommands[] = {
{ 0x80, 2, NULL }, /* Reset */
{ 0x07, 2, NULL }, /* MouseAction */
{ 0x08, 1, NULL }, /* RelMouseMode */
{ 0x09, 5, NULL }, /* AbsMouseMode */
{ 0x0A, 3, NULL }, /* MouseCursorKeycodes */
{ 0x0B, 3, NULL }, /* SetMouseThreshold */
{ 0x0C, 3, NULL }, /* SetMouseScale */
{ 0x0D, 1, NULL }, /* ReadAbsMousePos */
{ 0x0E, 6, NULL }, /* SetInternalMousePos */
{ 0x0F, 1, NULL }, /* SetYAxisDown */
{ 0x10, 1, NULL }, /* SetYAxisUp */
{ 0x11, 1, NULL }, /* StartKeyboardTransfer */
{ 0x12, 1, NULL }, /* TurnMouseOff */
{ 0x13, 1, NULL }, /* StopKeyboardTransfer */
{ 0x14, 1, NULL }, /* ReturnJoystickAuto */
{ 0x15, 1, NULL }, /* StopJoystick */
{ 0x16, 1, NULL }, /* ReturnJoystick */
{ 0x17, 2, NULL }, /* SetJoystickMonitoring */
{ 0x18, 1, NULL }, /* SetJoystickFireDuration */
{ 0x19, 7, NULL }, /* SetCursorForJoystick */
{ 0x1A, 1, NULL }, /* DisableJoysticks */
{ 0x1B, 7, NULL }, /* SetClock */
{ 0x1C, 1, NULL }, /* ReadClock */
{ 0x20, 4, NULL }, /* LoadMemory */
{ 0x21, 3, NULL }, /* ReadMemory */
{ 0x22, 3, NULL }, /* Execute */
/* Report messages */
{ 0x87, 1, NULL }, /* ReportMouseAction */
{ 0x88, 1, NULL }, /* ReportMouseMode */
{ 0x89, 1, NULL }, /* ReportMouseMode */
{ 0x8A, 1, NULL }, /* ReportMouseMode */
{ 0x8B, 1, NULL }, /* ReportMouseThreshold */
{ 0x8C, 1, NULL }, /* ReportMouseScale */
{ 0x8F, 1, NULL }, /* ReportMouseVertical */
{ 0x90, 1, NULL }, /* ReportMouseVertical */
{ 0x92, 1, NULL }, /* ReportMouseAvailability */
{ 0x94, 1, NULL }, /* ReportJoystickMode */
{ 0x95, 1, NULL }, /* ReportJoystickMode */
{ 0x99, 1, NULL }, /* ReportJoystickMode */
{ 0x9A, 1, NULL }, /* ReportJoystickAvailability */
{ 0xFF, 0, NULL } /* Terminator */
};
/* O(C) original: linear scan */
static int ikbd_find_command_linear(uint8_t cmd)
{
int i = 0;
while (KeyboardCommands[i].Command != 0xFF) {
if (KeyboardCommands[i].Command == cmd)
return i;
i++;
}
return -1;
}
/* O(1) patched: dispatch table */
static int IKBDCmdIndex[256];
static void ikbd_init_dispatch(void)
{
int i;
for (i = 0; i < 256; i++)
IKBDCmdIndex[i] = -1;
for (i = 0; KeyboardCommands[i].Command != 0xFF; i++)
IKBDCmdIndex[KeyboardCommands[i].Command] = i;
}
static int ikbd_find_command_dispatch(uint8_t cmd)
{
return IKBDCmdIndex[cmd];
}
/* Count iterations in linear scan for given command byte */
static int ikbd_linear_iterations(uint8_t cmd)
{
int i = 0, count = 0;
while (KeyboardCommands[i].Command != 0xFF) {
count++;
if (KeyboardCommands[i].Command == cmd)
return count;
i++;
}
return count; /* not found, scanned all */
}
int main(void)
{
int i, linear_idx, dispatch_idx;
int total_tests = 0, pass = 0;
int max_linear_iter = 0, total_linear_iter = 0;
ikbd_init_dispatch();
/* Test 1: dispatch table matches linear scan for all 256 byte values */
printf("Test 1: dispatch table correctness for all 256 command bytes\n");
for (i = 0; i < 256; i++) {
linear_idx = ikbd_find_command_linear((uint8_t)i);
dispatch_idx = ikbd_find_command_dispatch((uint8_t)i);
total_tests++;
if (linear_idx == dispatch_idx) {
pass++;
} else {
printf(" FAIL byte=0x%02x linear=%d dispatch=%d\n", i, linear_idx, dispatch_idx);
}
}
printf(" %d/%d passed\n", pass, total_tests);
assert(pass == total_tests);
/* Test 2: O(1) vs O(C) -- dispatch always 1 step, linear varies */
printf("Test 2: iteration count reduction\n");
{
/* Known commands: reset (first entry scanned), 0x9A (last entry scanned) */
int iter_first = ikbd_linear_iterations(0x80); /* 0x80 is first */
int iter_last = ikbd_linear_iterations(0x9A); /* 0x9A is near last */
int iter_unknown = ikbd_linear_iterations(0x01); /* unknown -- scans all */
/* Count total commands (excluding terminator) */
int num_cmds = 0;
while (KeyboardCommands[num_cmds].Command != 0xFF) num_cmds++;
printf(" first-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_first);
printf(" last-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_last);
printf(" unknown-cmd iterations: %d (linear) vs 1 (dispatch)\n", iter_unknown);
printf(" total commands in table: %d\n", num_cmds);
assert(iter_first >= 1);
assert(iter_last > iter_first); /* later entries cost more in linear */
assert(iter_unknown >= num_cmds); /* unknown scans everything */
/* dispatch is O(1): always 1 table lookup regardless */
total_linear_iter += iter_first + iter_last + iter_unknown;
max_linear_iter = iter_unknown;
printf(" PASS: dispatch eliminates up to %dx overhead vs worst-case linear\n",
max_linear_iter);
}
/* Test 3: NumParameters preserved correctly */
printf("Test 3: NumParameters preserved in dispatch table\n");
{
int idx;
/* 0x09 AbsMouseMode expects 5 parameters */
idx = ikbd_find_command_dispatch(0x09);
assert(idx >= 0);
assert(KeyboardCommands[idx].NumParameters == 5);
/* 0x19 SetCursorForJoystick expects 7 parameters */
idx = ikbd_find_command_dispatch(0x19);
assert(idx >= 0);
assert(KeyboardCommands[idx].NumParameters == 7);
/* 0x08 RelMouseMode expects 1 parameter */
idx = ikbd_find_command_dispatch(0x08);
assert(idx >= 0);
assert(KeyboardCommands[idx].NumParameters == 1);
printf(" PASS: NumParameters correct for 0x09 (5), 0x19 (7), 0x08 (1)\n");
}
/* Test 4: unknown commands return -1 */
printf("Test 4: unknown command bytes return -1\n");
{
/* 0x01, 0x02, 0x03 are not in the IKBD command set */
assert(ikbd_find_command_dispatch(0x01) == -1);
assert(ikbd_find_command_dispatch(0x02) == -1);
assert(ikbd_find_command_dispatch(0x03) == -1);
assert(ikbd_find_command_dispatch(0x00) == -1);
printf(" PASS: 0x00, 0x01, 0x02, 0x03 all return -1\n");
}
printf("\nAll tests passed. O(1) dispatch table eliminates O(%d) linear scan.\n",
max_linear_iter);
return 0;
}