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
Problem
metacall_value_create(id)returns a non-NULL but malformed value forMETACALL_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-corefork with branchmetacall-python-conversion-testRemove each type from the skip list one at a time. Test logs:
CLASS — segfault during invoke
OBJECT — segfault inside py_loader_impl_value_to_capi
EXCEPTION — bus error during metacall_value_destroy
THROWABLE — bus error during metacall_value_destroy
Pattern
uninitialized inner pointer when converting the value
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 --
Related