Stream: platform development

Topic: basic-webserver jwt segafult


view this post on Zulip Luke Boswell (Sep 04 2024 at 05:53):

Working on the JWT impl for basic-webserver, and I have a segfault I'm trying to track down.

@Brendan Hansknecht I've isolated it to the call to the following... does this look right? (I know there is a lot of context missing) .. specifically, I suspect the key here is being dropped at the end of this function and that is messing with the heap somehow. Is the data copied into the heap or just a reference?

#[roc_fn(name = "jwtDecodingKeyFromRsaPem")]
pub extern "C" fn jwtDecodingKeyFromRsaPem(
    secret: &RocStr,
) -> RocResult<RocBox<()>, glue_internal_jwt::JwtErr> {
    dbg!(&secret);

    let key = match jsonwebtoken::DecodingKey::from_rsa_pem(secret.as_bytes()) {
        Ok(key) => key,
        Err(err) => return RocResult::err(dbg!(jwt_err_to_roc(err))),
    };

    dbg!("IN HERE");

    let heap = jwt_key_heap();
    let alloc_result = heap.alloc_for(key);
    match alloc_result {
        Ok(out) => RocResult::ok(out),
        Err(_) => RocResult::err(glue_internal_jwt::JwtErr::Other(
            "Ran out of memory allocating space for DecodingKey".into(),
        )),
    }
}

I'm wondering if I need to tell rust not to drop this value somehow.

view this post on Zulip Notification Bot (Sep 04 2024 at 06:02):

This topic was moved here from #compiler development > bug possibly from using threadsafe refcounted heap by Luke Boswell.

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:04):

I've tried switching to use a ManuallyDrop, but that hasn't helped.

static JWT_KEY_HEAP: OnceLock<ThreadSafeRefcountedResourceHeap<ManuallyDrop<jsonwebtoken::DecodingKey>>>

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:04):

Link to PR https://github.com/roc-lang/basic-webserver/pull/72 in case that helps

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:06):

The heap takes ownership of the data passed in so it definitely shouldn't get dropped. Even if it was dropped, it shouldn't segfault until attempted use at some other point in the app

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:11):

My first guess would be that something is wrong with the return result type maybe the error tag specifically. But that is just a wild guess from what commonly goes wrong and can lead to segfaults

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:15):

Something fishy is going on... I change the type to jwtDecodingKeyFromRsaPem(secret: &RocStr) -> RocResult<RocBox<()>, ()> and now roc is seeing an err, even though it's returning an ok

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:15):

That's not surprising

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:16):

That would move the tag id location

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:16):

Also changed on the roc side

jwtDecodingKeyFromRsaPem : Str -> Task InternalJwt.DecodingKey {}

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:16):

Oh....hmmm :thinking:

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:17):

And the decoding key is just Box {}, right?

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:17):

Oh wait... I think I had InternalJwt.DecodingKey := Box {}

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:18):

Maybe that should be a : instead of :=

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:18):

I don't think it should make a difference here, but worth trying

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:19):

Yeah, hasn't changed anything

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:19):

I did simply the type so across the host boundary it's now just jwtDecodingKeyFromRsaPem : Str -> Task (Box {}) {}

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:20):

I can take a look tomorrow. No immediate ideas on a quick skim of the PR. Does the llvm ir type look normal?

Also, can you tell if it is segfaulting in rust or in roc?

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:22):

Valgrind may also help here

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:22):

commit 547975849af6860837d29e8870ee9dfc34b22d78

declare { [0 x i64], [1 x i64], i8, [7 x i8] } @roc_fx_jwtDecodingKeyFromRsaPem(ptr)

define internal fastcc void @roc_fx_jwtDecodingKeyFromRsaPem_fastcc_wrapper(ptr %0, ptr %1) {
entry:
  %tmp = call { [0 x i64], [1 x i64], i8, [7 x i8] } @roc_fx_jwtDecodingKeyFromRsaPem(ptr %0), !dbg !330
  store { [0 x i64], [1 x i64], i8, [7 x i8] } %tmp, ptr %1, align 8, !dbg !330
  ret void, !dbg !330
}

define internal fastcc void @PlatformTasks_jwtDecodingKeyFromRsaPem_48c2caee6f1010356bbec8845a6ee45f2928c63eece16acf25dc3f84dc5f6(ptr %closure_arg_jwtDecodingKeyFromRsaPem_0, ptr %0) !dbg !272 {
entry:
  tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) %0, ptr noundef nonnull align 8 dereferenceable(24) %closure_arg_jwtDecodingKeyFromRsaPem_0, i64 24, i1 false), !dbg !273
  ret void, !dbg !273
}

define internal fastcc void @PlatformTasks_task_closure_jwtDecodingKeyFromRsaPem_f752fd971dee73f4bef39e126f15a0a84437112755ca589db8702463ce739a({} %"130", ptr %closure_arg_jwtDecodingKeyFromRsaPem_0, ptr %0) !dbg !329 {
entry:
  %result_value = alloca { [0 x i64], [1 x i64], i8, [7 x i8] }, align 8
  call fastcc void @roc_fx_jwtDecodingKeyFromRsaPem_fastcc_wrapper(ptr %closure_arg_jwtDecodingKeyFromRsaPem_0, ptr nonnull %result_value), !dbg !330
  call fastcc void @"#Attr_#dec_2"(ptr %closure_arg_jwtDecodingKeyFromRsaPem_0), !dbg !330
  call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %0, ptr noundef nonnull align 8 dereferenceable(16) %result_value, i64 16, i1 false), !dbg !330
  ret void, !dbg !330
}

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:23):

This is the new Box {} version with error type of {}?

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:23):

Brendan Hansknecht said:

Valgrind may also help here

Ah, good idea... I should have thought of that. I'm just on my mac trying random things. I'll switch over to the linux machine

view this post on Zulip Brendan Hansknecht (Sep 04 2024 at 06:24):

Yeah, llvm ir type looks good

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:25):

I'm relieved to hear you say that... but also now more confused

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:42):

Here is the full valgrind output

And a snippet.

==118423== Conditional jump or move depends on uninitialised value(s)
==118423==    at 0x1AE641: roc_fx_jwtDecodingKeyFromRsaPem (in /home/lb-dev/github/basic-webserver/examples/json-web-token)
==118423==    by 0x12A873: roc__forHost_0_caller (in /home/lb-dev/github/basic-webserver/examples/json-web-token)
==118423==    by 0x175C01: rust_main (in /home/lb-dev/github/basic-webserver/examples/json-web-token)
==118423==    by 0x488110D: (below main) (in /nix/store/ddwyrxif62r8n6xclvskjyy6szdhvj60-glibc-2.39-5/lib/libc.so.6)

view this post on Zulip Luke Boswell (Sep 04 2024 at 06:42):

According to GPT-4o :smiley:

Potential Issues in Your Code

  1. Uninitialized Memory in RocStr:
    Ensure that the RocStr passed to the function is properly initialized. If RocStr is a custom type, make sure its internal data is correctly set up before being used.

  2. Uninitialized Memory in jwt_key_heap:
    The function jwt_key_heap() is called to get a heap allocator. Ensure that this allocator is properly initialized and that it doesn't return uninitialized memory.

  3. Uninitialized Memory in alloc_for:
    The alloc_for method is called on the heap allocator. Ensure that this method does not return uninitialized memory or depend on uninitialized values.

view this post on Zulip Luke Boswell (Sep 04 2024 at 07:02):

@Brendan Hansknecht if you could have a look tomorrow that would be super.

I've stripped it back to a minimal repro (commented everything else out)... and used only the most simple types RocStr RocBox<()> etc. I've double checked the flow and types and it all looks good to me... so I'm scratching my head a little confused how rust is returning an RocResult::ok but roc is getting an Task.err {}.

I'm going to take a break and pick this back up again tomorrow. I've pushed the latest I have for debugging to that branch.

view this post on Zulip Brendan Hansknecht (Sep 05 2024 at 22:13):

Took a bit of a look....not seeing the issue currently.

view this post on Zulip Brendan Hansknecht (Sep 05 2024 at 22:14):

One guess for why some of these things work in basic-cli but no here is that either roc_std or heap.rs have different versions and there is a bug.

view this post on Zulip Luke Boswell (Sep 05 2024 at 22:40):

Ohk, that makes

view this post on Zulip Luke Boswell (Sep 05 2024 at 22:40):

Gives me some ideas to test out

view this post on Zulip Luke Boswell (Sep 05 2024 at 22:47):

I tried using roc_std from roc-lang/roc, that hasn't helped. :sad:


Last updated: Jul 06 2025 at 12:14 UTC