Skip to content

Conversation

@PekingSpades
Copy link

Summary

This PR fixes critical memory safety issues in src/macos/keycode.c:

  1. 64-bit pointer size mismatch in CFDictionaryGetValueIfPresent call - The original code cast a CGKeyCode (uint16_t, 2 bytes) pointer to const void **, causing the function to write 8 bytes into a 2-byte variable on 64-bit systems. This results in stack corruption.

  2. Missing NULL checks in createStringForKey - TISCopyCurrentASCIICapableKeyboardInputSource() and TISGetInputSourceProperty() can return NULL, but the code directly dereferenced the results without checking, leading to potential crashes.

Related Issues

libnut-core

  • Related to nut-tree/libnut-core#187 - The build warnings in that issue mention incompatible pointer types in macOS code, which is symptomatic of the same class of pointer handling issues this PR addresses.

nut.js (parent project)

The keyCodeForChar function is critical for keyboard input functionality. While these issues may have multiple causes, fixing memory corruption in the keycode lookup could improve stability:

  • nut-tree/nut.js#618 - "@ symbol typed as '2' on Ubuntu (keyboard layout issue)" - Keyboard layout mapping issues
  • nut-tree/nut.js#535 - "Some characters are incorrectly typed" - Character typing issues on Linux
  • nut-tree/nut.js#536 - "Having performance issue in keyboard.type API" - Performance issues when passing strings to keyboard.type
  • nut-tree/nut.js#493 - "Typing in Mac menu bar does not work" - macOS-specific typing issues

Changes

keyCodeForChar function

Before (dangerous):

CGKeyCode code;  // 2 bytes
// Writes 8 bytes (pointer size) into 2-byte variable = stack corruption
if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr,
                                   (const void **)&code))

After (safe):

void *value = NULL;  // 8 bytes, matches pointer size
if (CFDictionaryGetValueIfPresent(charToCodeDict, charStr, (const void **)&value))
{
    code = (CGKeyCode)(uintptr_t)value;  // Safe conversion
}

createStringForKey function

Before:

TISInputSourceRef currentKeyboard = TISCopyCurrentASCIICapableKeyboardInputSource();
CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, ...);
// No NULL checks - crash if either returns NULL
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);

After:

TISInputSourceRef currentKeyboard = TISCopyCurrentASCIICapableKeyboardInputSource();
if (currentKeyboard == NULL) {
    return NULL;
}

CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, ...);
if (layoutData == NULL) {
    CFRelease(currentKeyboard);  // Avoid memory leak
    return NULL;
}

Documentation References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant