Skip to content

metacall_value_create returns malformed values for CLASS / OBJECT / EXCEPTION / THROWABLE that crash on invoke or destroy #811

@ROKUMATE

Description

@ROKUMATE

Problem

metacall_value_create(id) returns a non-NULL but malformed value for
METACALL_CLASS, METACALL_OBJECT, METACALL_EXCEPTION, METACALL_THROWABLE.
Any downstream use of those values crashes — either when a loader tries
to convert/invoke them, or when metacall_value_destroy is called.

The bug is in core (source/metacall/source/metacall_value.c around L140-150),
not loader-specific:

case METACALL_CLASS:
return value_create_class(class_create(NULL, ACCESSOR_TYPE_STATIC, NULL, NULL));

case METACALL_OBJECT:
return value_create_object(object_create("", ACCESSOR_TYPE_STATIC, NULL, NULL,
metacall_value_create(METACALL_CLASS)));

case METACALL_EXCEPTION:
return exception_create_const("", "", 0, ""); // missing value_create_exception wrapper

case METACALL_THROWABLE:
return throwable_create(metacall_value_create(METACALL_EXCEPTION));

EXCEPTION is the clearest bug: it returns a bare exception pointer cast
to value, not wrapped via value_create_exception, so downstream code
treats raw exception memory as a value struct.

This affects all loaders (verified for py; node/rb/ts have the same
core-side root cause and will hit equivalent crashes once their
conversion loops are exercised).

Repro

metacall_python_conversion_test in ROKUMATE/metacall-core fork with branch metacall-python-conversion-test
Remove each type from the skip list one at a time. Test logs:

CLASS — segfault during invoke

Testing 15 : Class
Stack trace (most recent call last):
metacallfv_s + 163                              (libmetacall.dylib)
function_py_interface_invoke + 311              (libpy_loader.so)
function_py_interface_invoke + 311              (libpy_loader.so)
SEGFAULT

OBJECT — segfault inside py_loader_impl_value_to_capi

Testing 16 : Object
Stack trace (most recent call last):
metacallfv_s + 163                              (libmetacall.dylib)
function_py_interface_invoke + 283              (libpy_loader.so)
py_loader_impl_value_to_capi + 851              (libpy_loader.so)
SEGFAULT

EXCEPTION — bus error during metacall_value_destroy

Testing 17 : Exception
Exception => UNSUPPORTED                        ← metacallhv returned NULL cleanly
Stack trace (most recent call last):
value_type_destroy + 603                        (libmetacall.dylib)
value_destroy + 51                              (libmetacall.dylib)
BUS ERROR

THROWABLE — bus error during metacall_value_destroy

Testing 18 : Throwable
Throwable => UNSUPPORTED                        ← metacallhv returned NULL cleanly
Stack trace (most recent call last):
value_type_destroy + 59                         (libmetacall.dylib)
BUS ERROR

Pattern

  • CLASS / OBJECT crash on INVOKE because the loader dereferences the
    uninitialized inner pointer when converting the value
  • EXCEPTION / THROWABLE crash on DESTROY because the type-specific
    destructor dereferences the uninitialized/raw inner pointer

Same root cause, different code paths trigger first.

Expected

metacall_value_create(id) for these 4 types should either:
(a) return NULL so callers can detect and skip (matches the
METACALL_SIZE / METACALL_INVALID default branch behavior), OR
(b) return a value with the inner pointer initialized to a state
that is safely invokable and safely destroyable.

Currently it returns values that crash on every downstream path.

Adjacent finding (same test run, py_loader-specific, not the same bug)

METACALL_FUTURE round-trips to METACALL_NULL through py_loader identity
call. Other types preserve their type through the round-trip:

Boolean => Boolean Char => Long Float => Double
Int => Long Long => Long Future => Null ← unexpected
String => String Array => Array Function => Function

This is a py_loader behavior (probably in capi_to_value / value_to_capi
handling of TYPE_FUTURE), distinct from the value_create bug above.
Mentioning it because the same test surfaced it; may want a separate
issue if you'd prefer.

Example test Error - local run --

Start 38: metacall-python-conversion-test
1/1 Test #38: metacall-python-conversion-test ...Bus error***Exception:   0.64 sec
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from metacall_python_conversion_test
[ RUN      ] metacall_python_conversion_test.DefaultConstructor
INT => Long
Testing 0 : Boolean
Boolean => Boolean
Testing 1 : Char
Char => Long
Testing 2 : Short
Short => Long
Testing 3 : Int
Int => Long
Testing 4 : Long
Long => Long
Testing 5 : Float
Float => Double
Testing 6 : Double
Double => Double
Testing 7 : String
String => String
Testing 8 : Buffer
Buffer => Buffer
Testing 9 : Array
Array => Array
Testing 10 : Map
Map => Map
Testing 11 : Pointer
Pointer => Pointer
Testing 12 : Future
Future => Null
Testing 13 : Function
Function => Function
Testing 14 : Null
Null => Null
Class => UNSUPPORTED
Object => UNSUPPORTED
Testing 17 : Exception
Exception => UNSUPPORTED
Stack trace (most recent call last):
#13   Object "metacall-python-conversion-test", at 0x10471dbff, in main + 31
#12   Object "libgtest.1.16.0.dylib", at 0x10494edef, in testing::UnitTest::Run() + 123
#11   Object "libgtest.1.16.0.dylib", at 0x10494eea7, in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) + 131
#10   Object "libgtest.1.16.0.dylib", at 0x10494f49f, in testing::internal::UnitTestImpl::RunAllTests() + 1127
#9    Object "libgtest.1.16.0.dylib", at 0x10493e3f3, in testing::TestSuite::Run() + 903
#8    Object "libgtest.1.16.0.dylib", at 0x10493d323, in testing::TestInfo::Run() + 359
#7    Object "libgtest.1.16.0.dylib", at 0x10493bf5b, in testing::Test::Run() + 219
#6    Object "libgtest.1.16.0.dylib", at 0x10493c08b, in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) + 131
#5    Object "metacall-python-conversion-test", at 0x10471e1bf, in metacall_python_conversion_test_DefaultConstructor_Test::TestBody() + 1043
#4    Object "libmetacall.dylib", at 0x104781de3, in value_type_destroy + 603
#3    Object "libmetacall.dylib", at 0x1047807b7, in value_destroy + 51
#2    Object "libsystem_platform.dylib", at 0x185573743, in _sigtramp + 55
#1    Object "libbacktrace_plugin.so", at 0x10480916b, in backward::SignalHandling::sig_handler(int, __siginfo*, void*) + 19
#0    Object "libbacktrace_plugin.so", at 0x1048091c3, in backward::SignalHandling::handleSignal(int, __siginfo*, void*) + 59


0% tests passed, 1 tests failed out of 1

Label Time Summary:
metacall-python-conversion-test    =   0.64 sec*proc (1 test)

Total Test time (real) =   0.69 sec

The following tests FAILED:
         38 - metacall-python-conversion-test (Bus error)       metacall-python-conversion-test
Errors while running CTest

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions