GET /v2/positionInfo/{marketIndex} triggers SIGSEGV (null pointer dereference) — gateway process killed
Summary
Calling GET /v2/positionInfo/{marketIndex} when an open perp position exists causes a segfault in libdrift_ffi_sys.so, killing the gateway process. The endpoint works correctly (returns HTTP 400 "no position") when no position exists, but crashes when it attempts to compute derived fields for an active position.
Environment
- Gateway version: v1.5.5 (commit
056c890)
- libdrift_ffi_sys: v2.158.0 (
sha256: 3725759a1dab275f087cac6afc694ce0ad94a6295bf40643664ad28ac7ef705d)
- Platform: Amazon Linux 2023, x86_64 (
6.1.163-186.299.amzn2023.x86_64)
- Docker: 25.0.14
- RPC: Helius mainnet-beta (paid tier)
- Mode: Delegate mode (
--delegate <DELEGATOR_PUBKEY>)
- Markets:
--markets btc-perp
Steps to reproduce
- Start the gateway in delegate mode with
--markets btc-perp
- Place a market order that fills (creates a perp position):
curl -X POST http://localhost:8080/v2/orders \
-H "Content-Type: application/json" \
-d '{"orders": [{"marketIndex": 1, "marketType": "perp", "amount": "0.0001", "orderType": "market", "userOrderId": 1}]}'
- Wait for the order to fill (~10–30s). Verify position exists:
curl http://localhost:8080/v2/positions
# Should show: {"perp": [{"amount": "0.0001", "averageEntry": "...", "marketIndex": 1}]}
- Call
positionInfo:
curl http://localhost:8080/v2/positionInfo/1
- Gateway process is killed.
curl receives an empty reply or connection refused.
Expected behavior
Returns JSON with extended position info (amount, averageEntry, liquidationPrice, unrealizedPnL, oraclePrice).
Actual behavior
The gateway process is killed by SIGSEGV. No error response is returned — the TCP connection drops. No panic or backtrace appears in gateway logs (even with RUST_BACKTRACE=1), because the crash occurs in native FFI code.
dmesg output
[13027.401426] traps: drift-gateway[14029] general protection fault ip:7f11cfe6250f sp:7ffe807c3b40 error:0 in libc.so.6[7f11cfe62000+156000]
[15446.066666] actix-rt|system[14300]: segfault at 0 ip 0000000000000000 sp 00007fefe614bbe0 error 14 in drift-gateway[561061274000+10e000] likely on CPU 1 (core 0, socket 0)
[15465.984002] actix-rt|system[15879]: segfault at 0 ip 0000000000000000 sp 00007fd1cd251be0 error 14 in drift-gateway[56002c285000+10e000] likely on CPU 1 (core 0, socket 0)
[75414.389951] actix-rt|system[44050]: segfault at 0 ip 0000000000000000 sp 00007fcb99ff2be0 error 14 in drift-gateway[557012d83000+10e000] likely on CPU 1 (core 0, socket 0)
[82546.455786] actix-rt|system[50510]: segfault at 0 ip 0000000000000000 sp 00007f66631f1be0 error 14 in drift-gateway[55555ed0b000+10e000] likely on CPU 1 (core 0, socket 0)
The ip 0000000000000000 indicates a null function pointer call. The crash originates in the actix-rt worker thread handling the HTTP request.
Root cause analysis
We traced the crash through the full call chain:
gateway: get_position_extended() [controller.rs]
→ calculate_liquidation_price_and_unrealized_pnl() [drift-rs: math/liquidation.rs]
→ calculate_liquidation_price_inner() [drift-rs: math/liquidation.rs]
→ calculate_margin_requirement_and_total_collateral_and_liability_info() [FFI into libdrift_ffi_sys.so]
→ SIGSEGV at ip 0x0000000000000000 [inside prebuilt .so]
The crash is inside libdrift_ffi_sys.so, not in the Rust code. Specifically, calculate_margin_requirement_and_total_collateral_and_liability_info (the FFI function exported from drift-ffi-sys/src/exports.rs) dereferences a null function pointer during margin computation.
We verified this by patching drift-rs — replacing the three .expect() calls in math/liquidation.rs with proper .ok_or_else() error propagation (jgustave/drift-rs@f0571f0). The gateway was rebuilt with this fix. The SIGSEGV persisted — confirming the crash is in the prebuilt FFI binary, not in the Rust wrapper's error handling.
There is also a secondary bug in drift-rs: the calculate_liquidation_price_and_unrealized_pnl function uses .expect("market exists") and .expect("oracle loaded") which can panic (and propagate undefined behavior across the FFI boundary) when market configs or oracles are not yet loaded. This is a separate issue from the SIGSEGV but should also be fixed. PR available: jgustave/drift-rs@f0571f0.
Related issues
Workaround
Avoid calling GET /v2/positionInfo/{marketIndex}. Use GET /v2/positions instead, which returns amount, averageEntry, and marketIndex without invoking the FFI computation. Compute unrealized PnL client-side from the average entry price and current oracle price. liquidationPrice is unavailable until this bug is fixed.
GET /v2/positionInfo/{marketIndex}triggers SIGSEGV (null pointer dereference) — gateway process killedSummary
Calling
GET /v2/positionInfo/{marketIndex}when an open perp position exists causes a segfault inlibdrift_ffi_sys.so, killing the gateway process. The endpoint works correctly (returns HTTP 400"no position") when no position exists, but crashes when it attempts to compute derived fields for an active position.Environment
056c890)sha256: 3725759a1dab275f087cac6afc694ce0ad94a6295bf40643664ad28ac7ef705d)6.1.163-186.299.amzn2023.x86_64)--delegate <DELEGATOR_PUBKEY>)--markets btc-perpSteps to reproduce
--markets btc-perpcurl http://localhost:8080/v2/positions # Should show: {"perp": [{"amount": "0.0001", "averageEntry": "...", "marketIndex": 1}]}positionInfo:curlreceives an empty reply or connection refused.Expected behavior
Returns JSON with extended position info (
amount,averageEntry,liquidationPrice,unrealizedPnL,oraclePrice).Actual behavior
The gateway process is killed by SIGSEGV. No error response is returned — the TCP connection drops. No panic or backtrace appears in gateway logs (even with
RUST_BACKTRACE=1), because the crash occurs in native FFI code.dmesg output
The
ip 0000000000000000indicates a null function pointer call. The crash originates in the actix-rt worker thread handling the HTTP request.Root cause analysis
We traced the crash through the full call chain:
The crash is inside
libdrift_ffi_sys.so, not in the Rust code. Specifically,calculate_margin_requirement_and_total_collateral_and_liability_info(the FFI function exported fromdrift-ffi-sys/src/exports.rs) dereferences a null function pointer during margin computation.We verified this by patching
drift-rs— replacing the three.expect()calls inmath/liquidation.rswith proper.ok_or_else()error propagation (jgustave/drift-rs@f0571f0). The gateway was rebuilt with this fix. The SIGSEGV persisted — confirming the crash is in the prebuilt FFI binary, not in the Rust wrapper's error handling.There is also a secondary bug in
drift-rs: thecalculate_liquidation_price_and_unrealized_pnlfunction uses.expect("market exists")and.expect("oracle loaded")which can panic (and propagate undefined behavior across the FFI boundary) when market configs or oracles are not yet loaded. This is a separate issue from the SIGSEGV but should also be fixed. PR available: jgustave/drift-rs@f0571f0.Related issues
types.rs:156, fixed in PR fix: avgEntryPrice divide by 0 #57). Same endpoint, different root cause.segfault at 0), possibly same root cause inlibdrift_ffi_sys.so.Workaround
Avoid calling
GET /v2/positionInfo/{marketIndex}. UseGET /v2/positionsinstead, which returnsamount,averageEntry, andmarketIndexwithout invoking the FFI computation. Compute unrealized PnL client-side from the average entry price and current oracle price.liquidationPriceis unavailable until this bug is fixed.