Skip to content

Native segfault in RpcServiceDescriptor.callables #663

@andrewparmet

Description

@andrewparmet

Describe the bug

I'm building Kotlin/Native gRPC support for protokt, a Kotlin Protocol Buffer compiler. protokt generates its own message types and uses the kotlinx-rpc compiler plugin to process @Grpc-annotated service interfaces for gRPC transport. I have it working on JVM but it segfaults on Native.

To Reproduce

  1. Kotlin version: 2.3.20
  2. Gradle version: 9.4.1
  3. OS: macOS (macosArm64 target)
  4. Minimal reproducer:
@Grpc
interface SimpleService {
    suspend fun echo(message: String): String
}

fun main() {
    val desc = serviceDescriptorOf(SimpleService::class)
    println(desc.fqName)              // works
    println(desc.getCallable("echo")) // works by reading from the callables map
    println(desc.callables)           // segfaults (exit 139)
}

Crash stack from the macOS crash report:

[?] offset 0
[grpc-krpc-native.kexe] kfun:kotlinx.rpc.grpc.descriptor.GrpcServiceDescriptor#<get-callables>(){}kotlin.collections.Map<kotlin.String,kotlinx.rpc.
descriptor.RpcCallable<1:0>>-trampoline
[grpc-krpc-native.kexe] kfun:kotlinx.rpc.grpc.server.internal.GrpcServerImpl.getDefinition#internal
[grpc-krpc-native.kexe] kfun:kotlinx.rpc.grpc.server.internal.GrpcServerImpl#registerService(kotlin.reflect.KClass<0:0>;kotlin.Function0<0:0>){0§<kotlin.Any>}

Expected behavior
descriptor.callables should return the map of callables, same as on JVM.

Additional context

The bug doesn't seem specific to protokt; I have a minimal reproducer that consumes the published 0.11.0-grpc-187 artifacts with no protokt involved. I was unable to reproduce the issue within the kotlinx-rpc project itself, which points to a subtler bug in the published artifacts.

Possible solution:

I have a minimal PR here that, when published locally, allows this branch of protokt to run successfully.

The failing invocation is ./examples/grpc-krpc-native/build/bin/macosArm64/debugExecutable/grpc-krpc-native.kexe, which creates a GrpcServer, registers a Greeter service with protokt messages, starts the server, connects a client, and calls SayHello.

Before, with 0.11.0-grpc-187:

Server started on port 50051
Exit: 139

After:

Server started on port 50051
Received: Hello Native World
Exit: 0

I've changed GrpcServerImpl to use delegate.methodNames instead of descriptor.callables. I haven't fully diagnosed why callables is broken. I've established that:

  • desc.fqName and desc.simpleName work (string properties on the same companion object)
  • desc.getCallable("echo") works (a function that reads from the callables backing field directly)
  • desc.callables segfaults at offset 0 in the itable trampoline
  • The crash is in the property getter dispatch
  • In the compiler plugin codegen, callables is created via generateMapProperty with visibility = PRIVATE then gets a PUBLIC getter added, while simpleName/fqName are created with visibility = PUBLIC from the start (generateStringOverriddenProperty). I tried making callables PUBLIC too but that didn't fix it.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions