]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kern/ledger.c
xnu-3789.51.2.tar.gz
[apple/xnu.git] / osfmk / kern / ledger.c
index 983e51b9c522d9c82431ca05cffdeb4a4da6fc9f..6b2434e0d4de799394ff95131630fc37f6d619b0 100644 (file)
@@ -44,6 +44,7 @@
 
 #include <libkern/OSAtomic.h>
 #include <mach/mach_types.h>
+#include <os/overflow.h>
 
 /*
  * Ledger entry flags. Bits in second nibble (masked by 0xF0) are used for
@@ -269,18 +270,24 @@ ledger_entry_add(ledger_template_t template, const char *key,
        /* If the table is full, attempt to double its size */
        if (template->lt_cnt == template->lt_table_size) {
                struct entry_template *new_entries, *old_entries;
-               int old_cnt, old_sz;
+               int old_cnt, old_sz, new_sz = 0;
                spl_t s;
 
                old_cnt = template->lt_table_size;
-               old_sz = (int)(old_cnt * sizeof (struct entry_template));
-               new_entries = kalloc(old_sz * 2);
+               old_sz = old_cnt * (int)(sizeof(struct entry_template));
+               /* double old_sz allocation, but check for overflow */
+               if (os_mul_overflow(old_sz, 2, &new_sz)) {
+                       template_unlock(template);
+                       return -1;
+               }
+               new_entries = kalloc(new_sz);
                if (new_entries == NULL) {
                        template_unlock(template);
-                       return (-1);
+                       return -1;
                }
                memcpy(new_entries, template->lt_entries, old_sz);
                memset(((char *)new_entries) + old_sz, 0, old_sz);
+               /* assume: if the sz didn't overflow, neither will the count */
                template->lt_table_size = old_cnt * 2;
 
                old_entries = template->lt_entries;
@@ -604,10 +611,11 @@ ledger_refill(uint64_t now, ledger_t ledger, int entry)
 
        balance = le->le_credit - le->le_debit;
        due = periods * le->le_limit;
+
        if (balance - due < 0)
                due = balance;
 
-       assert(due >= 0);
+       assertf(due >= 0,"now=%llu, ledger=%p, entry=%d, balance=%lld, due=%lld", now, ledger, entry, balance, due);
 
        OSAddAtomic64(due, &le->le_debit);
 
@@ -824,17 +832,32 @@ kern_return_t
 ledger_rollup(ledger_t to_ledger, ledger_t from_ledger)
 {
        int i;
-       struct ledger_entry *from_le, *to_le;
 
        assert(to_ledger->l_template == from_ledger->l_template);
 
        for (i = 0; i < to_ledger->l_size; i++) {
-               if (ENTRY_VALID(from_ledger, i) && ENTRY_VALID(to_ledger, i)) {
-                       from_le = &from_ledger->l_entries[i];
-                       to_le   =   &to_ledger->l_entries[i];
-                       OSAddAtomic64(from_le->le_credit, &to_le->le_credit);
-                       OSAddAtomic64(from_le->le_debit,  &to_le->le_debit);
-               }
+               ledger_rollup_entry(to_ledger, from_ledger, i);
+       }
+
+       return (KERN_SUCCESS);
+}
+
+/* Add one ledger entry value to another.
+ * They must have been created from the same template.
+ * Since the credit and debit values are added one
+ * at a time, other thread might read the a bogus value.
+ */
+kern_return_t
+ledger_rollup_entry(ledger_t to_ledger, ledger_t from_ledger, int entry)
+{
+       struct ledger_entry *from_le, *to_le;
+
+       assert(to_ledger->l_template == from_ledger->l_template);
+       if (ENTRY_VALID(from_ledger, entry) && ENTRY_VALID(to_ledger, entry)) {
+               from_le = &from_ledger->l_entries[entry];
+               to_le   =   &to_ledger->l_entries[entry];
+               OSAddAtomic64(from_le->le_credit, &to_le->le_credit);
+               OSAddAtomic64(from_le->le_debit,  &to_le->le_debit);
        }
 
        return (KERN_SUCCESS);
@@ -849,6 +872,7 @@ kern_return_t
 ledger_zero_balance(ledger_t ledger, int entry)
 {
        struct ledger_entry *le;
+       ledger_amount_t debit, credit;
 
        if (!ENTRY_VALID(ledger, entry))
                return (KERN_INVALID_VALUE);
@@ -856,18 +880,21 @@ ledger_zero_balance(ledger_t ledger, int entry)
        le = &ledger->l_entries[entry];
 
 top:
+       debit = le->le_debit;
+       credit = le->le_credit;
+
        if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
                assert(le->le_debit == 0);
-               if (!OSCompareAndSwap64(le->le_credit, 0, &le->le_credit)) {
+               if (!OSCompareAndSwap64(credit, 0, &le->le_credit)) {
                        goto top;
                }
                lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, 0));
-       } else if (le->le_credit > le->le_debit) {
-               if (!OSCompareAndSwap64(le->le_debit, le->le_credit, &le->le_debit))
+       } else if (credit > debit) {
+               if (!OSCompareAndSwap64(debit, credit, &le->le_debit))
                        goto top;
                lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_debit, le->le_credit));
-       } else if (le->le_credit < le->le_debit) {
-               if (!OSCompareAndSwap64(le->le_credit, le->le_debit, &le->le_credit))
+       } else if (credit < debit) {
+               if (!OSCompareAndSwap64(credit, debit, &le->le_credit))
                        goto top;
                lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, le->le_debit));
        }