aboutsummaryrefslogtreecommitdiff
path: root/libctf/ctf-create.c
diff options
context:
space:
mode:
authorNick Alcock <nick.alcock@oracle.com>2020-06-04 17:30:01 +0100
committerNick Alcock <nick.alcock@oracle.com>2020-07-22 18:02:18 +0100
commit1fa7a0c24e78e7f7995a3fa608c9f97a6ccbe1b6 (patch)
treea302cec9970964b2ccb806937b7daab8003dcfe7 /libctf/ctf-create.c
parent3166467b00a08050366120fc3cd64336a51fa12c (diff)
downloadbinutils-1fa7a0c24e78e7f7995a3fa608c9f97a6ccbe1b6.zip
binutils-1fa7a0c24e78e7f7995a3fa608c9f97a6ccbe1b6.tar.gz
binutils-1fa7a0c24e78e7f7995a3fa608c9f97a6ccbe1b6.tar.bz2
libctf: sort out potential refcount loops
When you link TUs that contain conflicting types together, the resulting CTF section is an archive containing many CTF dicts. These dicts appear in ctf_link_outputs of the shared dict, with each ctf_import'ing that shared dict. ctf_importing a dict bumps its refcount to stop it going away while it's in use -- but if the shared dict (whose refcount is bumped) has the child dict (doing the bumping) in its ctf_link_outputs, we have a refcount loop, since the child dict only un-ctf_imports and drops the parent's refcount when it is freed, but the child is only freed when the parent's refcount falls to zero. (In the future, this will be able to go wrong on the inputs too, when an ld -r'ed deduplicated output with conflicts is relinked. Right now this cannot happen because we don't ctf_import such dicts at all. This will be fixed in a later commit in this series.) Fix this by introducing an internal-use-only ctf_import_unref function that imports a parent dict *witthout* bumping the parent's refcount, and using it when we create per-CU outputs. This function is only safe to use if you know the parent cannot go away while the child exists: but if the parent *owns* the child, as here, this is necessarily true. Record in the ctf_file_t whether a parent was imported via ctf_import or ctf_import_unref, so that if you do another ctf_import later on (or a ctf_import_unref) it can decide whether to drop the refcount of the existing parent being replaced depending on which function you used to import that one. Adjust ctf_serialize so that rather than doing a ctf_import (which is wrong if the original import was ctf_import_unref'fed), we just copy the parent field and refcount over and forcibly flip the unref flag on on the old copy we are going to discard. ctf_file_close also needs a bit of tweaking to only close the parent if it was not imported with ctf_import_unref: while we're at it, guard against repeated closes with a refcount of zero and stop them causing double-frees, even if destruction of things freed *inside* ctf_file_close cause such recursion. Verified no leaks or accesses to freed memory after all of this with valgrind. (It was leak-happy before.) libctf/ * ctf-impl.c (ctf_file_t) <ctf_parent_unreffed>: New. (ctf_import_unref): New. * ctf-open.c (ctf_file_close) Drop the refcount all the way to zero. Don't recurse back in if the refcount is already zero. (ctf_import): Check ctf_parent_unreffed before deciding whether to close a pre-existing parent. Set it to zero. (ctf_import_unreffed): New, as above, setting ctf_parent_unreffed to 1. * ctf-create.c (ctf_serialize): Do not ctf_import into the new child: use direct assignment, and set unreffed on the new and old children. * ctf-link.c (ctf_create_per_cu): Import the parent using ctf_import_unreffed.
Diffstat (limited to 'libctf/ctf-create.c')
-rw-r--r--libctf/ctf-create.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index a538b2d..a964c20 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -516,8 +516,9 @@ ctf_serialize (ctf_file_t *fp)
}
(void) ctf_setmodel (nfp, ctf_getmodel (fp));
- (void) ctf_import (nfp, fp->ctf_parent);
+ nfp->ctf_parent = fp->ctf_parent;
+ nfp->ctf_parent_unreffed = fp->ctf_parent_unreffed;
nfp->ctf_refcnt = fp->ctf_refcnt;
nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY;
if (nfp->ctf_dynbase == NULL)
@@ -565,6 +566,7 @@ ctf_serialize (ctf_file_t *fp)
fp->ctf_syn_ext_strtab = NULL;
fp->ctf_link_cu_mapping = NULL;
fp->ctf_link_type_mapping = NULL;
+ fp->ctf_parent_unreffed = 1;
fp->ctf_dvhash = NULL;
memset (&fp->ctf_dvdefs, 0, sizeof (ctf_list_t));