[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

A nasty, silent native-component gotcha (GB_BASE) - (follow-up on the uvicorn thread)


Dear Benoît, dear Claus, dear all,

Following up on last month's thread about a Gambas-native uvicorn-style
HTTP server: I've been building a small proof-of-concept native
component (a Gambas binding around Node.js' llhttp library, for
incremental HTTP/1.1 parsing) and ran into a gotcha nasty enough that I
think it's worth sharing on the list, since it can burn anyone writing
a native component and gives essentially no signal until real, later
usage.

The mistake: my class' native struct did not have GB_BASE as its first
field.


typedef struct {
    llhttp_t parser;
    llhttp_settings_t settings;
    ...
} CLLHTTPPARSER;


instead of


typedef struct {
    GB_BASE ob;   /* forgot this */
    llhttp_t parser;
    llhttp_settings_t settings;
    ...
} CLLHTTPPARSER;


gambas.h is completely explicit about the requirement right next to
GB_BASE's own definition ("This structure represents the base of every
Gambas object. It must be placed in the beginning of all object
structure defined in a component."), so this is entirely my own
carelessness - but what struck me is how little feedback the mistake
gives you:

- gbc3 compiles the component without any warning.
- gbi3 generates .info/.list without complaint.
- The component loads fine, the class registers fine.
- Reading from the object is fine.
- A constructor that writes nothing at all to the object is fine.
- The very first ordinary field write (THIS->field = value, or a raw
  pointer write, doesn't matter which) succeeds without any visible
  problem at that point.

The crash only happens later, whenever gbx3 next touches its own
per-object bookkeeping (the class pointer and refcount that should have
lived in the GB_BASE that isn't there) - which your own field write has
just silently overwritten. The resulting SIGSEGV's backtrace points into
gbx3/gbr3 itself, with no visible connection to the line of code that
actually caused it.

This sent me on a long, wrong trail. My PoC is meant to run on a
slightly older 3.19 target, and I'd built it against a newer dev
checkout, so version-ABI mismatch was my prime suspect for quite a
while - especially since a first real-world test (an actual gbr3, not
the dev build) did crash. I only ruled that out by rebuilding against
the exact matching 3.19.5 release commit (same crash), and then
conclusively by recompiling gb.net's own unmodified source through my
own toolchain (worked perfectly) versus a trivial from-scratch test
class with zero relation to my original component (crashed identically
on any write). That comparison is what finally pointed at the missing
GB_BASE.

Two things I wanted to ask/suggest, now that I've found it:

1. Would a cheap sanity check be worth adding somewhere in
   CLASS_register_class (or right after a native _new call, in debug
   builds only) - e.g. checking that the class pointer embedded at the
   start of a freshly allocated object still matches what was written at
   allocation time? I don't know the internals well enough to judge the
   cost/complexity, but even a debug-only assertion would have saved me
   a good few hours, since it would point straight at the object instead
   of crashing somewhere unrelated much later.

2. Would it be worth a short, explicit warning on the component-writing
   wiki page (with exactly this failure mode described), for anyone
   writing their first native class the way I did? I'm happy to draft
   that if it's welcome - it's exactly the kind of thing that's obvious
   in hindsight and invisible beforehand.

Otherwise, my llhttp-based component parses real HTTP/1.1 requests
correctly now that this is fixed, so the small local web-server
proof-of-concept is moving forward again. I'll keep sharing progress on
this thread as it develops.

Best regards,
Marc


Follow-Ups:
Re: A nasty, silent native-component gotcha (GB_BASE) - (follow-up on the uvicorn thread)Benoît Minisini <benoit.minisini@xxxxxxxxxxxxxxxx>