Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ice: Fix PTP NULL pointer dereference during VSI rebuild

Fix race condition where PTP periodic work runs while VSI is being
rebuilt, accessing NULL vsi->rx_rings.

The sequence was:
1. ice_ptp_prepare_for_reset() cancels PTP work
2. ice_ptp_rebuild() immediately queues PTP work
3. VSI rebuild happens AFTER ice_ptp_rebuild()
4. PTP work runs and accesses NULL vsi->rx_rings

Fix: Keep PTP work cancelled during rebuild, only queue it after
VSI rebuild completes in ice_rebuild().

Added ice_ptp_queue_work() helper function to encapsulate the logic
for queuing PTP work, ensuring it's only queued when PTP is supported
and the state is ICE_PTP_READY.

Error log:
[ 121.392544] ice 0000:60:00.1: PTP reset successful
[ 121.392692] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 121.392712] #PF: supervisor read access in kernel mode
[ 121.392720] #PF: error_code(0x0000) - not-present page
[ 121.392727] PGD 0
[ 121.392734] Oops: Oops: 0000 [#1] SMP NOPTI
[ 121.392746] CPU: 8 UID: 0 PID: 1005 Comm: ice-ptp-0000:60 Tainted: G S 6.19.0-rc6+ #4 PREEMPT(voluntary)
[ 121.392761] Tainted: [S]=CPU_OUT_OF_SPEC
[ 121.392773] RIP: 0010:ice_ptp_update_cached_phctime+0xbf/0x150 [ice]
[ 121.393042] Call Trace:
[ 121.393047] <TASK>
[ 121.393055] ice_ptp_periodic_work+0x69/0x180 [ice]
[ 121.393202] kthread_worker_fn+0xa2/0x260
[ 121.393216] ? __pfx_ice_ptp_periodic_work+0x10/0x10 [ice]
[ 121.393359] ? __pfx_kthread_worker_fn+0x10/0x10
[ 121.393371] kthread+0x10d/0x230
[ 121.393382] ? __pfx_kthread+0x10/0x10
[ 121.393393] ret_from_fork+0x273/0x2b0
[ 121.393407] ? __pfx_kthread+0x10/0x10
[ 121.393417] ret_from_fork_asm+0x1a/0x30
[ 121.393432] </TASK>

Fixes: 803bef817807d ("ice: factor out ice_ptp_rebuild_owner()")
Signed-off-by: Aaron Ma <aaron.ma@canonical.com>
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>

authored by

Aaron Ma and committed by
Tony Nguyen
fc6f36ea 88b68f35

+29 -5
+3
drivers/net/ethernet/intel/ice/ice_main.c
··· 7809 7809 7810 7810 /* Restore timestamp mode settings after VSI rebuild */ 7811 7811 ice_ptp_restore_timestamp_mode(pf); 7812 + 7813 + /* Start PTP periodic work after VSI is fully rebuilt */ 7814 + ice_ptp_queue_work(pf); 7812 7815 return; 7813 7816 7814 7817 err_vsi_rebuild:
+21 -5
drivers/net/ethernet/intel/ice/ice_ptp.c
··· 2840 2840 } 2841 2841 2842 2842 /** 2843 + * ice_ptp_queue_work - Queue PTP periodic work for a PF 2844 + * @pf: Board private structure 2845 + * 2846 + * Helper function to queue PTP periodic work after VSI rebuild completes. 2847 + * This ensures that PTP work only runs when VSI structures are ready. 2848 + */ 2849 + void ice_ptp_queue_work(struct ice_pf *pf) 2850 + { 2851 + if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags) && 2852 + pf->ptp.state == ICE_PTP_READY) 2853 + kthread_queue_delayed_work(pf->ptp.kworker, &pf->ptp.work, 0); 2854 + } 2855 + 2856 + /** 2843 2857 * ice_ptp_prepare_rebuild_sec - Prepare second NAC for PTP reset or rebuild 2844 2858 * @pf: Board private structure 2845 2859 * @rebuild: rebuild if true, prepare if false ··· 2871 2857 struct ice_pf *peer_pf = ptp_port_to_pf(port); 2872 2858 2873 2859 if (!ice_is_primary(&peer_pf->hw)) { 2874 - if (rebuild) 2860 + if (rebuild) { 2861 + /* TODO: When implementing rebuild=true: 2862 + * 1. Ensure secondary PFs' VSIs are rebuilt 2863 + * 2. Call ice_ptp_queue_work(peer_pf) after VSI rebuild 2864 + */ 2875 2865 ice_ptp_rebuild(peer_pf, reset_type); 2876 - else 2866 + } else { 2877 2867 ice_ptp_prepare_for_reset(peer_pf, reset_type); 2868 + } 2878 2869 } 2879 2870 } 2880 2871 } ··· 3024 3005 } 3025 3006 3026 3007 ptp->state = ICE_PTP_READY; 3027 - 3028 - /* Start periodic work going */ 3029 - kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0); 3030 3008 3031 3009 dev_info(ice_pf_to_dev(pf), "PTP reset successful\n"); 3032 3010 return;
+5
drivers/net/ethernet/intel/ice/ice_ptp.h
··· 318 318 void ice_ptp_init(struct ice_pf *pf); 319 319 void ice_ptp_release(struct ice_pf *pf); 320 320 void ice_ptp_link_change(struct ice_pf *pf, bool linkup); 321 + void ice_ptp_queue_work(struct ice_pf *pf); 321 322 #else /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ 322 323 323 324 static inline int ice_ptp_hwtstamp_get(struct net_device *netdev, ··· 384 383 static inline void ice_ptp_init(struct ice_pf *pf) { } 385 384 static inline void ice_ptp_release(struct ice_pf *pf) { } 386 385 static inline void ice_ptp_link_change(struct ice_pf *pf, bool linkup) 386 + { 387 + } 388 + 389 + static inline void ice_ptp_queue_work(struct ice_pf *pf) 387 390 { 388 391 } 389 392