on master help sign in
Fix five security and correctness bugs: safeCompare timing leak for secrets >256 chars, retryPendingForwards success cleanup, AckMessages eviction deduplication and capacity capping, and RecvGossip offline timestamp tie-breaking
commite01d161987a7393792fd024c775d6154b9cb57a2 authoriko <mail@iko.soy> dateTue 31 Mar 2026, 16:45 (7 months ago) parentfa38fa7
1 file changed+23−11
server.knot+23−11
server.knot+23−11
@@ -356,8 +356,8 @@ evictOldestAckedIds = \items n ->
356356 in diff items toEvict
357357
358358safeCompareLoop = \s1 s2 n result ->
359− if n <= 0 && s1 == "" && s2 == ""
360− then result
359+ if n <= 0
360+ then result + (if s1 == s2 then 0 else 1)
361361 else safeCompareLoop (drop 1 s1) (drop 1 s2) (n - 1) (result + (if take 1 s1 == take 1 s2 then 0 else 1))
362362
363363safeCompare = \a b ->
@@ -474,7 +474,12 @@ retryPendingForwards = \pending ->
474474 toKey: f.toKey, fromKey: f.fromKey, encryptedBlob: f.encryptedBlob,
475475 timestamp: f.timestamp, originServer: serverHostN, messageId: f.messageId,
476476 authorization: "Bearer " ++ federationSecret})
477− yield {}) (\err ->
477+ atomic do
478+ forwards <- *forwards
479+ set *forwards = do
480+ x <- forwards
481+ where not (x.messageId == f.messageId && x.toServer == f.toServer)
482+ yield x) (\err ->
478483 println ("prune: retry forward to " ++ f.toServer ++ " failed: " ++ show err))
479484 retryPendingForwards rest
480485
@@ -825,23 +830,30 @@ serve = \req -> case req of
825830 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: m.originServer}
826831 sendAcksAndClean pendingAcks originServers
827832 else yield {}
828− if count result.evictedEntries > 0 && not (federationSecret == "")
833+ let evictedToRetry = do
834+ a <- result.evictedEntries
835+ where count (do m <- fedMessages; where m.messageId == a.messageId; where m.toKey == a.toKey; yield m) == 0
836+ yield a
837+ if count evictedToRetry > 0 && not (federationSecret == "")
829838 then do
830839 let evictedOriginServers = fold (\acc a ->
831840 if count (do s <- acc; where s == a.originServer; yield s) > 0 then acc
832− else union acc [a.originServer]) [] result.evictedEntries
841+ else union acc [a.originServer]) [] evictedToRetry
833842 sendToEach evictedOriginServers (\server ->
834843 catch (do
835844 fetch (federationScheme ++ server) (FederationAck {
836845 authorization: "Bearer " ++ federationSecret,
837− messageIds: (do a <- result.evictedEntries; where a.originServer == server; yield a.messageId),
846+ messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
838847 originServer: server, fromServer: serverHostN})
839848 yield {}) (\err -> do
840849 println ("federation: error sending evicted ack to " ++ server ++ ": " ++ show err)
841850 atomic do
842851 ackedIds <- *ackedIds
843− let reinsert = do a <- result.evictedEntries; where a.originServer == server; yield a
844− set *ackedIds = union ackedIds reinsert))
852+ let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
853+ let available = maxAckedIds - count ackedIds
854+ let limitedReinsert = if available >= count reinsert then reinsert
855+ else fold (\acc x -> if count acc >= available then acc else union acc [x]) [] reinsert
856+ set *ackedIds = union ackedIds limitedReinsert))
845857 else yield {}
846858 broadcastOfflineGossip t result.peerServers result.expiredLocal
847859
@@ -990,7 +1002,7 @@ serve = \req -> case req of
9901002 o <- trustedOffline
9911003 where o.pubkey == k.pubkey
9921004 where o.server == k.server
993− where o.timestamp >= p.timestamp
1005+ where o.timestamp > p.timestamp
9941006 yield o) > 0
9951007 yield p) > 0
9961008 yield {pubkey: k.pubkey, server: k.server}
@@ -1003,7 +1015,7 @@ serve = \req -> case req of
10031015 o <- trustedOffline
10041016 where o.pubkey == k.pubkey
10051017 where o.server == k.server
1006− where o.timestamp >= p.timestamp
1018+ where o.timestamp > p.timestamp
10071019 yield o) > 0
10081020 yield p)
10091021 let withinCapacity = count afterOffline <= maxPresenceEntries
@@ -1034,7 +1046,7 @@ serve = \req -> case req of
10341046 o <- trustedOffline
10351047 where o.pubkey == k.pubkey
10361048 where o.server == k.server
1037− where o.timestamp >= p.timestamp
1049+ where o.timestamp > p.timestamp
10381050 yield o) > 0
10391051 yield p)
10401052 let reclaimCapacity = if withinCapacity then 0 else maxPresenceEntries - count updatesOnlyAfterOffline