Skip to content

gfapi: copy the gfid-req into the xdata an open may carry past the caller's frame - #4810

Draft
ThalesBarretto wants to merge 1 commit into
gluster:develfrom
ThalesBarretto:fix/gfapi-open-xdata-copy-gfid-req
Draft

ThalesBarretto wants to merge 1 commit into
gluster:develfrom
ThalesBarretto:fix/gfapi-open-xdata-copy-gfid-req

Conversation

@ThalesBarretto

Copy link
Copy Markdown
Contributor

gfapi: copy the gfid-req into the xdata an open may carry past the caller's frame

glfs_creat() and the *at() entry helper store a pointer to a stack uuid_t in xattr_req as "gfid-req"
(dict_set_gfuuid(..., true)); when the file exists the same dict goes to the open, which open-behind answers first
and performs later, from an io-thread, after the caller returned - dict_to_xdr() then reads the gfid out of a dead
stack frame (ASan stack-use-after-return on gfapi-ssl-test.t, upcall-register-api.t, glfsxmp.t with open-behind
on; 16 bytes of dead stack on the wire otherwise, ignored by the server). Details in #4809.

The change (api/src/glfs-fops.c, +29/-5)

At the two sites whose xattr_req can reach an open, pub_glfs_creat() and setup_entry_fopat_args() (used by
glfs_openat()), allocate the gfid (GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t)) and set it with
dict_set_gfuuid(..., false), so the dict owns and frees it - the pattern features/shard and features/gfid-access
already use for their gfid-req. dict_set_gfuuid() never copies (true borrows the caller's pointer, false takes
ownership), so the stack uuid_t is replaced, not re-flagged; the *at() helper still fills its caller's uuid_t
argument. The create/mkdir/mknod/symlink and resolver producers keep the caller blocked until their fop completes and
are left unchanged.

Test

No new test: the defect is only observable with AddressSanitizer, where the existing tests/basic/gfapi/gfapi-ssl-test.t,
upcall-register-api.t and glfsxmp.t already fail before and pass after this change (open-behind on, its default).

Fixes: #4809

glfs_creat() and the *at() entry helper generate the gfid of a file to
be created into a uuid_t on the caller's stack and store a pointer to it
in xattr_req as "gfid-req" (dict_set_gfuuid(..., true): the dict keeps
the caller's pointer without copying it). When the file already exists
the same xattr_req is passed to the open (glfs_creat(), and glfs_openat()
with O_CREAT), and an open is a fop a translator may answer first and
perform later: open-behind does exactly that, doing the real open from
an io-thread on the first fop that needs it, after glfs_creat() or
glfs_openat() have returned. dict_to_xdr() then copies the gfid-req out
of a stack frame that no longer exists.

AddressSanitizer reports it as a stack-use-after-return in dict_to_xdr()
on every gfapi test that creates an already existing file with
open-behind on (tests/basic/gfapi/gfapi-ssl-test.t,
upcall-register-api.t, glfsxmp.t); without instrumentation it puts 16
bytes of dead stack on the wire as the gfid-req of an OPEN, which the
server ignores, so it has been silent.

Allocate the gfid and hand it to the dict with is_static=false, so the
dict owns and frees it, the way shard and gfid-access already set their
gfid-req. The other gfid-req producers in api/src (create, mkdir, mknod,
symlink, the resolver's lookups) feed fops whose caller stays blocked
until the fop completes, so their dicts cannot outlive the frame; they
are left unchanged.

Fixes: gluster#4809
Signed-off-by: Thales Antunes de Oliveira Barretto <thales.barretto.git@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gfapi: glfs_creat()/glfs_openat() store a stack pointer as "gfid-req" in the xdata of an open; open-behind replays it after the caller returned

1 participant