]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_object.h
xnu-3789.70.16.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_object.h
index 62e8bc253cd3f3b0d07aecee91085003a87d31d4..aaddd33c2b65f2553a80d1cd490acc3088480e24 100644 (file)
@@ -102,7 +102,7 @@ struct ipc_object {
        ipc_object_bits_t io_bits;
        ipc_object_refs_t io_references;
        lck_spin_t      io_lock_data;
-} __attribute__((__packed__));
+};
 
 /*
  * If another object type needs to participate in io_kotype()-based
@@ -173,15 +173,20 @@ extern void       io_free(
        lck_spin_lock(&(io)->io_lock_data)
 #define        io_lock_try(io) \
        lck_spin_try_lock(&(io)->io_lock_data)
+#define io_lock_held_kdp(io) \
+       kdp_lck_spin_is_acquired(&(io)->io_lock_data)
 #define        io_unlock(io) \
        lck_spin_unlock(&(io)->io_lock_data)
 
 #define _VOLATILE_ volatile
 
 /* Sanity check the ref count.  If it is 0, we may be doubly zfreeing.
- * If it is larger than max int, it has been corrupted, probably by being
- * modified into an address (this is architecture dependent, but it's
- * safe to assume there cannot really be max int references).
+ * If it is larger than max int, it has been corrupted or leaked,
+ * probably by being modified into an address (this is architecture
+ * dependent, but it's safe to assume there cannot really be max int
+ * references unless some code is leaking the io_reference without leaking
+ * object). Saturate the io_reference on release kernel if it reaches
+ * max int to avoid use after free.
  *
  * NOTE: The 0 test alone will not catch double zfreeing of ipc_port
  * structs, because the io_references field is the first word of the struct,
@@ -192,18 +197,42 @@ extern void       io_free(
 
 static inline void
 io_reference(ipc_object_t io) {
+       ipc_object_refs_t new_io_references;
+       ipc_object_refs_t old_io_references;
+
        assert((io)->io_references > 0 &&
            (io)->io_references < IO_MAX_REFERENCES);
-       OSIncrementAtomic(&((io)->io_references));
+
+       do {
+               old_io_references = (io)->io_references;
+               new_io_references = old_io_references + 1;
+               if (old_io_references == IO_MAX_REFERENCES) {
+                       break;
+               }
+       } while (OSCompareAndSwap(old_io_references, new_io_references,
+                       &((io)->io_references)) == FALSE);
 }
 
 
 static inline void
 io_release(ipc_object_t io) {
+       ipc_object_refs_t new_io_references;
+       ipc_object_refs_t old_io_references;
+
        assert((io)->io_references > 0 &&
            (io)->io_references < IO_MAX_REFERENCES);
+
+       do {
+               old_io_references = (io)->io_references;
+               new_io_references = old_io_references - 1;
+               if (old_io_references == IO_MAX_REFERENCES) {
+                       break;
+               }
+       } while (OSCompareAndSwap(old_io_references, new_io_references,
+                       &((io)->io_references)) == FALSE);
+
         /* If we just removed the last reference count */
-       if ( 1 == OSDecrementAtomic(&((io)->io_references))) {
+       if (1 == old_io_references) {
                /* Free the object */
                io_free(io_otype((io)), (io));
        }