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

[Xen-devel] [PATCH v2] libxl: enforce prohibitions of internal callers



libxl_internal.h says:

 * Functions using LIBXL__INIT_EGC may *not* generally be called from
 * within libxl, because libxl__egc_cleanup may call back into the
 * application. ...

and

 *                    ...  [Functions which take an ao_how] MAY NOT
 * be called from inside libxl, because they can cause reentrancy
 * callbacks.

However, this was not enforced.  Particularly the latter restriction
is easy to overlook, especially since during the transition period to
the new event system we have bent this rule a couple of times, and the
bad pattern simply involves passing 0 or NULL for the ao_how.

So use the compiler to enforce this property, as follows:

 - Mark all functions which take a libxl_asyncop_how, or which
   use EGC_INIT or LIBXL__INIT_EGC, with a new annotation
   LIBXL_EXTERNAL_CALLERS_ONLY in the public header.

 - Change the documentation comment for asynch operations and egcs to
   say that this should always be done.

 - Arrange that if libxl.h is included via libxl_internal.h,
   LIBXL_EXTERNAL_CALLERS_ONLY expands to __attribute__((warning(...))),
   which generates a message like this:
      libxl.c:1772: warning: call to 'libxl_device_disk_remove'
             declared with attribute warning:
             may not be called from within libxl
   Otherwise, the annotation expands to nothing, so external
   callers are unaffected.

 - Forbid inclusion of both libxl.h and libxl_internal.h unless
   libxl_internal.h came first, so that the above check doesn't have
   any loopholes.  Files which include libxl_internal.h should not
   include libxl.h as well.

   This is enforced explicitly using #error.  However, in practice
   with the current tree it just changes the error message when this
   mistake is made; otherwise we would carry on to immediately
   following #define which would cause the compiler to complain that
   LIBXL_EXTERNAL_CALLERS_ONLY was redefined.  Then the developer
   might be tempted to add a #ifndef which would be wrong - it would
   leave the affected translation unit unprotected by the new
   enforcement regime.  So let's be explicit.

 - Fix the one source of files which violate the above principle, the
   output from the idl compiler, by removing the redundant inclusion
   of libxl.h from the output.

   
Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Roger Pau Monne <roger.pau@xxxxxxxxxx>
Cc: Ian Campbell <ian.campbell@xxxxxxxxxx>

-
Changes in v2:
 * Rebased to current xen-unstable tip.
 * Added LIBXL_EXTERNAL_CALLERS_ONLY to more functions.
 * -Wno-error is no longer needed as the tree now has no errors of this form.
 * check-libxl-api-rules parses #... directives in the preprocessor output
   so that it can report errors at their location in libxl.h.
 * Indentation in check-libxl-api-rules fixed.

---
 .gitignore                        |    1 +
 .hgignore                         |    1 +
 tools/libxl/Makefile              |   14 +++++++-
 tools/libxl/check-libxl-api-rules |   23 +++++++++++++
 tools/libxl/gentypes.py           |    1 -
 tools/libxl/libxl.h               |   64 ++++++++++++++++++++++++++-----------
 tools/libxl/libxl_event.h         |   21 ++++++++----
 tools/libxl/libxl_internal.h      |   14 ++++++--
 8 files changed, 106 insertions(+), 33 deletions(-)

diff --git a/.gitignore b/.gitignore
index 0891d90..41c89c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -187,6 +187,7 @@ tools/libxl/xl
 tools/libxl/testenum
 tools/libxl/testenum.c
 tools/libxl/tmp.*
+tools/libxl/libxl.api-for-check
 tools/libaio/src/*.ol
 tools/libaio/src/*.os
 tools/misc/cpuperf/cpuperf-perfcntr
diff --git a/.hgignore b/.hgignore
index 9baf57b..8ff83c7 100644
--- a/.hgignore
+++ b/.hgignore
@@ -185,6 +185,7 @@
 ^tools/libxl/testidl\.c$
 ^tools/libxl/tmp\..*$
 ^tools/libxl/.*\.new$
+^tools/libxl/libxl\.api-for-check
 ^tools/libvchan/vchan-node[12]$
 ^tools/libaio/src/.*\.ol$
 ^tools/libaio/src/.*\.os$
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 424a7ee..395187f 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -74,7 +74,8 @@ LIBXL_OBJS += _libxl_types.o libxl_flask.o 
_libxl_types_internal.o
 $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) 
$(CFLAGS_libxenstore) $(CFLAGS_libblktapctl) -include $(XEN_ROOT)/tools/config.h
 
 AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \
-       _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h
+       _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h \
+        libxl.api-ok
 AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c
 AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c
 LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
@@ -113,6 +114,15 @@ $(LIBXL_OBJS) $(LIBXLU_OBJS) $(XL_OBJS) 
$(SAVE_HELPER_OBJS): $(AUTOINCS)
 genpath-target = $(call buildmakevars2file,_paths.h.tmp)
 $(eval $(genpath-target))
 
+libxl.api-ok: check-libxl-api-rules libxl.api-for-check
+       perl $^
+
+%.api-for-check: %.h
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \
+               -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \
+               >$@.new
+       $(call move-if-changed,$@.new,$@)
+
 _paths.h: genpath
        sed -e "s/\([^=]*\)=\(.*\)/#define \1 \2/g" $@.tmp >$@.2.tmp
        rm -f $@.tmp
@@ -200,7 +210,7 @@ install: all
 .PHONY: clean
 clean:
        $(RM) -f _*.h *.o *.so* *.a $(CLIENTS) $(DEPS)
-       $(RM) -f _*.c *.pyc _paths.*.tmp
+       $(RM) -f _*.c *.pyc _paths.*.tmp *.api-for-check
        $(RM) -f testidl.c.new testidl.c
 #      $(RM) -f $(AUTOSRCS) $(AUTOINCS)
 
diff --git a/tools/libxl/check-libxl-api-rules 
b/tools/libxl/check-libxl-api-rules
new file mode 100755
index 0000000..18ff39c
--- /dev/null
+++ b/tools/libxl/check-libxl-api-rules
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+use strict;
+our $needed=0;
+our $speclineoffset=0;
+our $specfile;
+while (<>) {
+    if (m/^\# (\d+) \"(.*)\"$/) {
+        $speclineoffset = $1 - $. -1;
+        $specfile = $2;
+    }
+    my $file = defined($specfile) ? $specfile : $ARGV;
+    my $line = $speclineoffset + $.;
+    if (m/libxl_asyncop_how[^;]/) {
+        $needed=1;
+    }
+    if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) {
+        $needed=0;
+    }
+    next unless $needed;
+    if (m/\;/) {
+        die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY";
+    }
+}
diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py
index eb67524..1d13201 100644
--- a/tools/libxl/gentypes.py
+++ b/tools/libxl/gentypes.py
@@ -377,7 +377,6 @@ if __name__ == '__main__':
 #include <stdlib.h>
 #include <string.h>
 
-#include "libxl.h"
 #include "libxl_internal.h"
 
 #define LIBXL_DTOR_POISON 0xa5
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index e1a7296..5ec2d74 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -266,6 +266,13 @@
 #endif
 #endif
 
+/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
+ * called from within libxl itself. Callers outside libxl, who
+ * do not #include libxl_internal.h, are fine. */
+#ifndef LIBXL_EXTERNAL_CALLERS_ONLY
+#define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */
+#endif
+
 typedef uint8_t libxl_mac[6];
 #define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
 #define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */
@@ -496,11 +503,13 @@ int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */);
 int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
                             uint32_t *domid,
                             const libxl_asyncop_how *ao_how,
-                            const libxl_asyncprogress_how *aop_console_how);
+                            const libxl_asyncprogress_how *aop_console_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
                                 uint32_t *domid, int restore_fd,
                                 const libxl_asyncop_how *ao_how,
-                                const libxl_asyncprogress_how 
*aop_console_how);
+                                const libxl_asyncprogress_how *aop_console_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
   /* A progress report will be made via ao_console_how, of type
    * domain_create_console_available, when the domain's primary
    * console is available and can be connected to.
@@ -511,7 +520,8 @@ void libxl_domain_config_dispose(libxl_domain_config 
*d_config);
 
 int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd,
                          int flags, /* LIBXL_SUSPEND_* */
-                         const libxl_asyncop_how *ao_how);
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
 #define LIBXL_SUSPEND_DEBUG 1
 #define LIBXL_SUSPEND_LIVE 2
 
@@ -523,12 +533,14 @@ int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, 
int suspend_cancel);
 
 int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
                              uint32_t domid, int send_fd, int recv_fd,
-                             const libxl_asyncop_how *ao_how);
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 
 int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid);
 int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid);
 int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
-                         const libxl_asyncop_how *ao_how);
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, 
libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid);
 
 /* get max. number of cpus supported by hypervisor */
@@ -549,7 +561,8 @@ int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid);
 
 int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
                            const char *filename,
-                           const libxl_asyncop_how *ao_how);
+                           const libxl_asyncop_how *ao_how)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
 
 int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint32_t 
target_memkb);
 int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int32_t 
target_memkb, int relative, int enforce);
@@ -677,13 +690,16 @@ void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int 
nr_vcpus);
 /* Disks */
 int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid,
                           libxl_device_disk *disk,
-                          const libxl_asyncop_how *ao_how);
+                          const libxl_asyncop_how *ao_how)
+                          LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid,
                              libxl_device_disk *disk,
-                             const libxl_asyncop_how *ao_how);
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid,
                               libxl_device_disk *disk,
-                              const libxl_asyncop_how *ao_how);
+                              const libxl_asyncop_how *ao_how)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
 
 libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int 
*num);
 int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
@@ -694,17 +710,21 @@ int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t 
domid,
  * be attached to the guest.
  */
 int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
-                       const libxl_asyncop_how *ao_how);
+                       const libxl_asyncop_how *ao_how)
+                       LIBXL_EXTERNAL_CALLERS_ONLY;
 
 /* Network Interfaces */
 int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
-                         const libxl_asyncop_how *ao_how);
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid,
                             libxl_device_nic *nic,
-                            const libxl_asyncop_how *ao_how);
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid,
                              libxl_device_nic *nic,
-                             const libxl_asyncop_how *ao_how);
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 
 libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int 
*num);
 int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
@@ -712,23 +732,29 @@ int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t 
domid,
 
 /* Keyboard */
 int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb,
-                         const libxl_asyncop_how *ao_how);
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid,
                             libxl_device_vkb *vkb,
-                            const libxl_asyncop_how *ao_how);
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid,
                              libxl_device_vkb *vkb,
-                             const libxl_asyncop_how *ao_how);
+                             const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
 
 /* Framebuffer */
 int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb,
-                         const libxl_asyncop_how *ao_how);
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid,
                             libxl_device_vfb *vfb,
-                            const libxl_asyncop_how *ao_how);
+                            const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid,
                              libxl_device_vfb *vfb,
-                             const libxl_asyncop_how *ao_how);
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 
 /* PCI Passthrough */
 int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci 
*pcidev);
diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
index 713d96d..3344bc8 100644
--- a/tools/libxl/libxl_event.h
+++ b/tools/libxl/libxl_event.h
@@ -37,7 +37,8 @@ typedef int libxl_event_predicate(const libxl_event*, void 
*user);
 
 int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
                       uint64_t typemask,
-                      libxl_event_predicate *predicate, void *predicate_user);
+                      libxl_event_predicate *predicate, void *predicate_user)
+                      LIBXL_EXTERNAL_CALLERS_ONLY;
   /* Searches for an event, already-happened, which matches typemask
    * and predicate.  predicate==0 matches any event.
    * libxl_event_check returns the event, which must then later be
@@ -48,7 +49,8 @@ int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
 
 int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
                      uint64_t typemask,
-                     libxl_event_predicate *predicate, void *predicate_user);
+                     libxl_event_predicate *predicate, void *predicate_user)
+                     LIBXL_EXTERNAL_CALLERS_ONLY;
   /* Like libxl_event_check but blocks if no suitable events are
    * available, until some are.  Uses libxl_osevent_beforepoll/
    * _afterpoll so may be inefficient if very many domains are being
@@ -256,7 +258,8 @@ struct pollfd;
  */
 int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
                              struct pollfd *fds, int *timeout_upd,
-                             struct timeval now);
+                             struct timeval now)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 
 /* nfds and fds[0..nfds] must be from the most recent call to
  * _beforepoll, as modified by poll.  (It is therefore not possible
@@ -271,7 +274,8 @@ int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
  * libxl_event_check.
  */
 void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd 
*fds,
-                             struct timeval now);
+                             struct timeval now)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 
 
 typedef struct libxl_osevent_hooks {
@@ -357,14 +361,16 @@ void libxl_osevent_register_hooks(libxl_ctx *ctx,
  */
 
 void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl,
-                               int fd, short events, short revents);
+                               int fd, short events, short revents)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
 
 /* Implicitly, on entry to this function the timeout has been
  * deregistered.  If _occurred_timeout is called, libxl will not
  * call timeout_deregister; if it wants to requeue the timeout it
  * will call timeout_register again.
  */
-void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl);
+void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl)
+                                    LIBXL_EXTERNAL_CALLERS_ONLY;
 
 
 /*======================================================================*/
@@ -506,7 +512,8 @@ void libxl_childproc_setmode(libxl_ctx *ctx, const 
libxl_childproc_hooks *hooks,
  * certainly need to use the self-pipe trick (or a working pselect or
  * ppoll) to implement this.
  */
-int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status);
+int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status)
+                           LIBXL_EXTERNAL_CALLERS_ONLY;
 
 
 /*
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 691b4f6..58004b3 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -52,6 +52,12 @@
 
 #include <xen/io/xenbus.h>
 
+#ifdef LIBXL_H
+# error libxl.h should be included via libxl_internal.h, not separately
+#endif
+#define LIBXL_EXTERNAL_CALLERS_ONLY \
+    __attribute__((warning("may not be called from within libxl")))
+
 #include "libxl.h"
 #include "_paths.h"
 #include "_libxl_save_msgs_callout.h"
@@ -1534,10 +1540,10 @@ int libxl__hotplug_settings(libxl__gc *gc, 
xs_transaction_t t);
  *
  * Functions using LIBXL__INIT_EGC may *not* generally be called from
  * within libxl, because libxl__egc_cleanup may call back into the
- * application.  This should be documented near the function
- * prototype(s) for callers of LIBXL__INIT_EGC and EGC_INIT.  You
- * should in any case not find it necessary to call egc-creators from
- * within libxl.
+ * application.  This should be enforced by declaring all such
+ * functions in libxl.h or libxl_event.h with
+ * LIBXL_EXTERNAL_CALLERS_ONLY.  You should in any case not find it
+ * necessary to call egc-creators from within libxl.
  *
  * The callbacks must all take place with the ctx unlocked because
  * the application is entitled to reenter libxl from them.  This
-- 
tg: (a7f0910..) t/xen/xl.external-callers-only (depends on: 
t/xen/xl.bootloader.fix.no-blunder-on)

Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.