diff --git a/rts/Capability.c b/rts/Capability.c
index 6cc112f7f03a61e176b9a4d9de7ddee9982b6ed5..65cb2fbbbaf5c4006e25ad651e04419f1fa47b69 100644
--- a/rts/Capability.c
+++ b/rts/Capability.c
@@ -418,36 +418,44 @@ void
 moreCapabilities (uint32_t from USED_IF_THREADS, uint32_t to USED_IF_THREADS)
 {
 #if defined(THREADED_RTS)
-    uint32_t i;
-    Capability **old_capabilities = capabilities;
+    Capability **new_capabilities = stgMallocBytes(to * sizeof(Capability*), "moreCapabilities");
 
-    capabilities = stgMallocBytes(to * sizeof(Capability*), "moreCapabilities");
+    // We must disable the timer while we do this since the tick handler may
+    // call contextSwitchAllCapabilities, which may see the capabilities array
+    // as we free it. The alternative would be to protect the capabilities
+    // array with a lock but this seems more expensive than necessary.
+    // See #17289.
+    stopTimer();
 
     if (to == 1) {
         // THREADED_RTS must work on builds that don't have a mutable
         // BaseReg (eg. unregisterised), so in this case
         // capabilities[0] must coincide with &MainCapability.
-        capabilities[0] = &MainCapability;
+        new_capabilities[0] = &MainCapability;
         initCapability(&MainCapability, 0);
     }
     else
     {
-        for (i = 0; i < to; i++) {
+        for (uint32_t i = 0; i < to; i++) {
             if (i < from) {
-                capabilities[i] = old_capabilities[i];
+                new_capabilities[i] = capabilities[i];
             } else {
-                capabilities[i] = stgMallocBytes(sizeof(Capability),
-                                                 "moreCapabilities");
-                initCapability(capabilities[i], i);
+                new_capabilities[i] = stgMallocBytes(sizeof(Capability),
+                                                     "moreCapabilities");
+                initCapability(new_capabilities[i], i);
             }
         }
     }
 
     debugTrace(DEBUG_sched, "allocated %d more capabilities", to - from);
 
+    Capability **old_capabilities = ACQUIRE_LOAD(&capabilities);
+    RELEASE_STORE(&capabilities, new_capabilities);
     if (old_capabilities != NULL) {
         stgFree(old_capabilities);
     }
+
+    startTimer();
 #endif
 }
 
diff --git a/rts/Timer.c b/rts/Timer.c
index c4439057427895beff56703ab595e6af4db4f27f..554a3fff2df51b0e1179bad44ec712f431657836 100644
--- a/rts/Timer.c
+++ b/rts/Timer.c
@@ -55,7 +55,7 @@ handle_tick(int unused STG_UNUSED)
 {
   handleProfTick();
   if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0
-      && RELAXED_LOAD(&timer_disabled) == 0)
+      && SEQ_CST_LOAD(&timer_disabled) == 0)
   {
       ticks_to_ctxt_switch--;
       if (ticks_to_ctxt_switch <= 0) {
@@ -119,7 +119,7 @@ initTimer(void)
     if (RtsFlags.MiscFlags.tickInterval != 0) {
         initTicker(RtsFlags.MiscFlags.tickInterval, handle_tick);
     }
-    RELAXED_STORE(&timer_disabled, 1);
+    SEQ_CST_STORE(&timer_disabled, 1);
 }
 
 void