From 1c560052a8c14cb389535d9c5e663e9a448d5796 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 15 Mar 2019 09:41:18 -0500 Subject: [PATCH] object: Add sanity check on correct parent class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking that the derived class is larger than the requested parent class saves us from some obvious mistakes, but as written, it does not catch all the cases; in particular, it is easy to forget to update a VIR_CLASS_NEW when changing the 'parent' member from virObject to virObjectLockabale, but where the size checks don't catch that. Add a parameter for one more layer of sanity checking. It would be cool if we could get gcc to stringize typeof(parent) into the string name of that type, so that we could confirm that the precise parent class is in use rather than just a struct that happens to have the same size as the parent class. But sizeof checks are better than nothing. Note that I did NOT change the fact that we require derived classes to be larger (as the difference in size makes it easy to tell classes apart), which means that even if a derived class has no functionality to add (but rather exists for compiler-enforced type-safety), it must still include a dummy member. But I did fix the wording of the error message to match the code. Signed-off-by: Eric Blake Reviewed-by: Ján Tomko Reviewed-by: Daniel P. Berrangé --- src/util/virobject.c | 9 ++++++--- src/util/virobject.h | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/util/virobject.c b/src/util/virobject.c index 3b28331ba7..919519735a 100644 --- a/src/util/virobject.c +++ b/src/util/virobject.c @@ -78,6 +78,7 @@ virObjectOnceInit(void) if (!(virObjectClass = virClassNew(NULL, "virObject", sizeof(virObject), + 0, NULL))) return -1; @@ -159,6 +160,7 @@ virClassPtr virClassNew(virClassPtr parent, const char *name, size_t objectSize, + size_t parentSize, virObjectDisposeCallback dispose) { virClassPtr klass; @@ -167,10 +169,11 @@ virClassNew(virClassPtr parent, STRNEQ(name, "virObject")) { virReportInvalidNonNullArg(parent); return NULL; - } else if (parent && - objectSize <= parent->objectSize) { + } else if (objectSize <= parentSize || + parentSize != (parent ? parent->objectSize : 0)) { + sa_assert(parent); virReportInvalidArg(objectSize, - _("object size %zu of %s is smaller than parent class %zu"), + _("object size %zu of %s is not larger than parent class %zu"), objectSize, name, parent->objectSize); return NULL; } diff --git a/src/util/virobject.h b/src/util/virobject.h index d4ec943a43..757068fcc1 100644 --- a/src/util/virobject.h +++ b/src/util/virobject.h @@ -82,12 +82,15 @@ virClassPtr virClassForObjectRWLockable(void); */ # define VIR_CLASS_NEW(name, prnt) \ verify_expr(offsetof(name, parent) == 0, \ - (name##Class = virClassNew(prnt, #name, sizeof(name), name##Dispose))) + (name##Class = virClassNew(prnt, #name, sizeof(name), \ + sizeof(((name *)NULL)->parent), \ + name##Dispose))) virClassPtr virClassNew(virClassPtr parent, const char *name, size_t objectSize, + size_t parentSize, virObjectDisposeCallback dispose) VIR_PARENT_REQUIRED ATTRIBUTE_NONNULL(2);