On Tue, Jun 11, 2019 at 10:33:18AM -0700, Junio C Hamano wrote: > Matthew DeVore <matvore@xxxxxxxxxxx> writes: > > >> convention, and it may even be a useful one (i.e. it allows you to > >> calloc() a structure with an embedded strbuf in it and the "if > >> .buf==NULL, call strbuf_init() lazily" can become an established > >> pattern), but at the same time it feels a bit brittle. > > > > Is it brittle because a strbuf may be initialized to non-zero memory, and so > > the "if (buf.buf == NULL)" may evaluate to false, and then go on treating > > garbage like a valid buffer? > > It is brittle because callers are bound to forget doing "if > (!x->buf.buf) lazy_init(&x->buf)" at some point, and blindly use an > uninitialized x->buf. Making sure x->buf is always initialized A corallary proposition would be to make this particular strbuf a "struct strbuf *" rather than an inline strbuf. It should then be rather clear to users that it may be null. Then whoever allocates the memory can also do the strbuf_init one-liner. The free'ing logic of list_objects_filter_options then only becomes trivially more complicated than it was before. Does that sound like a good compromise to you? > before any caller touches is the only way to solve it, and as you > said, there are two possible ways to make that happen. One way that > does not violate the current API contract is to make sure whoever > allocates and/or initializes the surrounding struct that embeds a > strbuf does strbuf_init(&x->buf) before any user sees the struct. The thing I don't like about that is that the non-zeroness of its initialization percolates upward to whatever the top-level struct is, which means implementation details leak a lot. This seems quite brittle as well, since anyone can forget to initialize some struct in the nested line. > > Another would be to update strbuf API so that strbuf_init() does not > even have to use slopbuf. But that is a much larger change that > potentially breaks existing users of strbuf API. When you have a > strbuf that has been prepared to be usable, the current API contract > allows its users to expect buf.buf is never NULL, so they assume > that they can safely write "if (!buf.buf)", so auditing strbuf.c and > making sure a strbuf with buf==NULL gets lazily initialized is not > enough. That's true. I didn't think it matters in the case of filter_spec in particular, since users of list_objects_filter_options are supposed to use an accessor and not touch the strbuf directly, but looking at it like a more general API change, it seems risky.