@@ -793,11 +793,13 @@ enum class MacAdvanceResult
793793 *
794794 * @param mc MegaClient for file access and async queue.
795795 * @param state The MAC computation state (must have cipher params initialized).
796+ * Passed by value to ensure the state stays alive during the function,
797+ * even if another thread resets the original shared_ptr.
796798 * @param logPrefix Prefix for log messages.
797799 * @return MacAdvanceResult indicating current state.
798800 */
799801MacAdvanceResult advanceMacComputation (MegaClient& mc,
800- std::shared_ptr<MacComputationState>& state,
802+ std::shared_ptr<MacComputationState> state,
801803 const std::string& logPrefix)
802804{
803805 if (!state)
@@ -987,7 +989,9 @@ FsCloudComparisonResult asyncMacComputation(MegaClient& mc,
987989 // Check for existing computation
988990 if (syncNode.hasRare () && syncNode.rareRO ().macComputation )
989991 {
990- auto & macComp = syncNode.rare ().macComputation ;
992+ // Take a copy for consistency with clone candidate pattern (not strictly required
993+ // since sync core is single-threaded, but defensive and documents intent)
994+ auto macComp = syncNode.rare ().macComputation ;
991995
992996 // Validate context is still current (handles moves, deletes, content changes)
993997 if (!macComp->contextMatches (fs.fsid , cn.handle , fs.fingerprint , cn.fingerprint ))
@@ -1089,6 +1093,10 @@ FsCloudComparisonResult asyncMacComputation(MegaClient& mc,
10891093 return {NODE_COMP_EREAD, INVALID_META_MAC, INVALID_META_MAC, FingerprintMismatch::Other};
10901094 }
10911095
1096+ // Mark initialization as complete (for consistency with clone candidate case,
1097+ // even though the sync core case is single-threaded)
1098+ macComp->setInitializationComplete ();
1099+
10921100 return {NODE_COMP_PENDING, INVALID_META_MAC, INVALID_META_MAC, FingerprintMismatch::Other};
10931101}
10941102
@@ -1186,34 +1194,36 @@ CloneMacStatus initCloneCandidateMacComputation(MegaClient& mc, SyncUpload_inCli
11861194
11871195 // Create computation state (no context needed - upload lifetime handles validity)
11881196 const m_off_t fileSize = upload.size ;
1189- upload.macComputation = std::make_shared<MacComputationState>(fileSize,
1190- upload.getLocalname (),
1191- mc.syncs .mMacComputationThrottle );
1197+ auto macComp = std::make_shared<MacComputationState>(fileSize,
1198+ upload.getLocalname (),
1199+ mc.syncs .mMacComputationThrottle );
1200+ upload.macComputation = macComp; // Store in upload, but keep local copy for safety
11921201
11931202 // Initialize cipher params from candidate node
1203+ // Using local macComp pointer ensures the object stays alive even if another thread
1204+ // resets upload.macComputation during this function.
11941205 const auto & nodeKey = candidateNode->nodekey ();
1195- auto & macComp = *upload.macComputation ;
11961206
1197- macComp. cloneCandidateHandle = candidateNode->nodeHandle ();
1198- macComp. cloneCandidateNodeKey = nodeKey;
1207+ macComp-> cloneCandidateHandle = candidateNode->nodeHandle ();
1208+ macComp-> cloneCandidateNodeKey = nodeKey;
11991209
1200- memcpy (macComp. transferkey .data (), nodeKey.data (), SymmCipher::KEYLENGTH);
1210+ memcpy (macComp-> transferkey .data (), nodeKey.data (), SymmCipher::KEYLENGTH);
12011211 SymmCipher::xorblock ((const byte*)nodeKey.data () + SymmCipher::KEYLENGTH,
1202- macComp. transferkey .data ());
1212+ macComp-> transferkey .data ());
12031213
12041214 const char * iva = nodeKey.data () + SymmCipher::KEYLENGTH;
1205- macComp. ctriv = MemAccess::get<int64_t >(iva);
1215+ macComp-> ctriv = MemAccess::get<int64_t >(iva);
12061216
12071217 // Try to acquire throttle now; if not, leave computation set up and return Pending
12081218 if (mc.syncs .macComputationThrottle ().tryAcquireFile ())
12091219 {
1210- macComp. throttleSlotAcquired = true ;
1220+ macComp-> throttleSlotAcquired = true ;
12111221
12121222 LOG_debug << logPre << " Initiating: " << upload.getLocalname () << " [size=" << fileSize
12131223 << " , files=" << mc.syncs .macComputationThrottle ().currentFiles () << " ]" ;
12141224
1215- // Start first chunk
1216- auto result = advanceMacComputation (mc, upload. macComputation , logPre);
1225+ // Start first chunk - pass local macComp to ensure object stays alive
1226+ auto result = advanceMacComputation (mc, macComp , logPre);
12171227 if (result == MacAdvanceResult::Failed)
12181228 {
12191229 LOG_debug << logPre << " Failed to start computation of MAC for "
@@ -1228,19 +1238,33 @@ CloneMacStatus initCloneCandidateMacComputation(MegaClient& mc, SyncUpload_inCli
12281238 << " [files=" << mc.syncs .macComputationThrottle ().currentFiles () << " ]" ;
12291239 }
12301240
1241+ // Mark initialization as complete - checkPendingCloneMac can now safely proceed
1242+ macComp->setInitializationComplete ();
1243+
12311244 return CloneMacStatus::Pending; // Computation created; may be waiting for throttle or running
12321245}
12331246
12341247CloneMacStatus checkPendingCloneMac (MegaClient& mc, SyncUpload_inClient& upload)
12351248{
12361249 static const std::string logPre{" checkPendingCloneMac: " };
12371250
1238- if (!upload.macComputation )
1251+ // Take a copy of the shared_ptr to ensure the MacComputationState stays alive
1252+ // during this function, even if another thread resets upload.macComputation.
1253+ // This also makes the null check and subsequent use atomic with respect to
1254+ // other threads that might reset the shared_ptr.
1255+ auto macComp = upload.macComputation ;
1256+ if (!macComp)
12391257 {
12401258 return CloneMacStatus::NoCandidates;
12411259 }
12421260
1243- auto & macComp = upload.macComputation ;
1261+ // Wait until initCloneCandidateMacComputation has finished setting up the computation.
1262+ // This prevents races where we try to process/advance a computation that's still
1263+ // being initialized on the client thread.
1264+ if (!macComp->isInitializationComplete ())
1265+ {
1266+ return CloneMacStatus::Pending;
1267+ }
12441268
12451269 if (upload.mMetaMac .has_value ())
12461270 {
0 commit comments