DF-3826 / swfw_double_release.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /* * DF-3826 PoC โ logic-level reproduction of the SW_FW_SYNC double-release in * e1000_read_i2c_byte_generic() (sys/dev/netif/ig_hal/e1000_82575.c:3270-3349) * * The HAL function's control flow is replicated 1:1 with mocked acquire/ * release primitives. When every I2C transfer attempt fails (the classic * "no SFP module / wedged I2C bus" case), the `fail:` label releases the * SWFW PHY0 semaphore for each of the 10 attempts, and then the * unconditional post-loop release at e1000_82575.c:3344 releases it an 11th * time for only 10 successful acquisitions. * * A correct implementation releases exactly once per successful acquire * (compare e1000_write_i2c_byte_generic, which acquires once before the loop * and releases once after it โ e1000_82575.c:3373-3417). * * On real hardware the extra release clears the E1000_SWFW_PHY0_SM bit in * E1000_SW_FW_SYNC even when this function no longer owns it, breaking the * SW/FW mutual-exclusion protocol if another port / the NVM path re-acquired * the resource in between (the two releases are separated by ~100ms of * msec_delay + e1000_i2c_bus_clear register pounding). * * exit code 0 = double release demonstrated; 1 = not demonstrated. */ #include <stdio.h> static int acquire_count, release_count; static int acquire_fail; /* mock: hw contention */ static int i2c_step_fail; /* mock: every I2C op fails */ static int mock_acquire(void) { if (acquire_fail) return -1; /* E1000_ERR_SWFW_SYNC */ acquire_count++; return 0; } static void mock_release(void) { release_count++; } static int i2c_start(void) { return i2c_step_fail; } static int clock_out_byte(int b){ return i2c_step_fail; (void)b; } static int get_ack(void) { return i2c_step_fail; } static int clock_in_byte(void) { return i2c_step_fail; } static int clock_out_bit(int b) { return i2c_step_fail; (void)b; } static void i2c_stop(void) { } /* * Control flow copied verbatim (minus DEBUGOUT/msec_delay) from * e1000_read_i2c_byte_generic(), e1000_82575.c:3284-3344. */ static int read_i2c_byte_replica(void) { int status = 0; const unsigned max_retry = 10; unsigned retry = 1; do { if (mock_acquire() != 0) { status = -1; goto read_byte_out; /* no release: OK */ } if (i2c_start()) goto fail; if (clock_out_byte(0xa0)) goto fail; if (get_ack()) goto fail; if (clock_out_byte(0x00)) goto fail; if (get_ack()) goto fail; if (i2c_start()) goto fail; if (clock_out_byte(0xa1)) goto fail; if (get_ack()) goto fail; if (clock_in_byte()) goto fail; if (clock_out_bit(1)) goto fail; i2c_stop(); break; /* success */ fail: mock_release(); /* e1000_82575.c:3333 */ /* msec_delay(100); e1000_i2c_bus_clear(hw); */ retry++; } while (retry < max_retry); mock_release(); /* e1000_82575.c:3344 */ read_byte_out: return status; } int main(void) { int rc = 1; /* Case 1: every I2C attempt fails โ the exhaustion path. */ acquire_count = release_count = 0; acquire_fail = 0; i2c_step_fail = 1; read_i2c_byte_replica(); printf("case1 (all I2C ops fail, retries exhausted): acquires=%d releases=%d -> %s\n", acquire_count, release_count, (release_count > acquire_count) ? "DOUBLE RELEASE" : "balanced"); if (release_count == acquire_count + 1) rc = 0; /* Case 2 (control): transfer succeeds on the first try. */ acquire_count = release_count = 0; acquire_fail = 0; i2c_step_fail = 0; read_i2c_byte_replica(); printf("case2 (first attempt succeeds): acquires=%d releases=%d -> %s\n", acquire_count, release_count, (release_count == acquire_count) ? "balanced" : "UNBALANCED"); if (release_count != acquire_count) rc &= ~1; /* keep rc 0 only if case1 showed the bug */ /* Case 3 (control): acquire itself fails โ must not release. */ acquire_count = release_count = 0; acquire_fail = 1; read_i2c_byte_replica(); printf("case3 (acquire fails): acquires=%d releases=%d -> %s\n", acquire_count, release_count, (release_count == acquire_count) ? "balanced" : "UNBALANCED"); return (rc == 0 && release_count == acquire_count) ? 0 : ((rc == 0) ? 0 : 1); } |