Skip to content

Commit b7551a4

Browse files
authored
fix: prevent index out of range panic in chat ls command (issue iyear#1112) (iyear#1115)
* fix: prevent index out of range panic in chat ls command - Added length check before accessing topics.Messages slice - Prevents panic when topics.Messages is empty (length 0) - Fixes 'index out of range [-1]' error in fetchTopics function The bug occurred at line 286 where topics.Messages[len(topics.Messages)-1] was accessed without checking if the slice was empty first. When the slice is empty, len-1 equals -1, causing a panic. Fixes iyear#1122 * fix: panic index out of range in fetchTopics
1 parent 8d1eeab commit b7551a4

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

app/chat/ls.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,30 @@ func processChannel(ctx context.Context, api *tg.Client, id int64, entities peer
249249

250250
// fetchTopics https://github.com/telegramdesktop/tdesktop/blob/4047f1733decd5edf96d125589f128758b68d922/Telegram/SourceFiles/data/data_forum.cpp#L135
251251
func fetchTopics(ctx context.Context, api *tg.Client, c tg.InputChannelClass) ([]Topic, error) {
252+
log := logctx.From(ctx)
252253
res := make([]Topic, 0)
253254
limit := 100 // why can't we use 500 like tdesktop?
254255
offsetTopic, offsetID, offsetDate := 0, 0, 0
256+
lastOffsetTopic := -1 // Track the last offsetTopic to detect infinite loops
257+
258+
// Track seen offsetTopics to detect cycles
259+
seenOffsets := make(map[int]bool)
255260

256261
for {
262+
// Detect infinite loop: if offsetTopic hasn't changed or we've seen it before
263+
if offsetTopic == lastOffsetTopic && lastOffsetTopic != -1 {
264+
log.Warn("pagination stuck (same offset), breaking loop",
265+
zap.Int("offset_topic", offsetTopic))
266+
break
267+
}
268+
if seenOffsets[offsetTopic] {
269+
log.Warn("pagination cycle detected, breaking loop",
270+
zap.Int("offset_topic", offsetTopic))
271+
break
272+
}
273+
seenOffsets[offsetTopic] = true
274+
lastOffsetTopic = offsetTopic
275+
257276
req := &tg.ChannelsGetForumTopicsRequest{
258277
Channel: c,
259278
Limit: limit,
@@ -267,6 +286,11 @@ func fetchTopics(ctx context.Context, api *tg.Client, c tg.InputChannelClass) ([
267286
return nil, errors.Wrap(err, "get forum topics")
268287
}
269288

289+
// If no topics returned, we're done
290+
if len(topics.Topics) == 0 {
291+
break
292+
}
293+
270294
for _, tp := range topics.Topics {
271295
if t, ok := tp.(*tg.ForumTopic); ok {
272296
res = append(res, Topic{
@@ -278,13 +302,30 @@ func fetchTopics(ctx context.Context, api *tg.Client, c tg.InputChannelClass) ([
278302
}
279303
}
280304

305+
// Safety break if we've collected all topics
306+
if len(res) >= topics.Count {
307+
break
308+
}
309+
281310
// last page
282311
if len(topics.Topics) < limit {
283312
break
284313
}
285314

286-
if lastMsg, ok := topics.Messages[len(topics.Messages)-1].AsNotEmpty(); ok {
287-
offsetID, offsetDate = lastMsg.GetID(), lastMsg.GetDate()
315+
// Update offset using last message if available
316+
// Use a local variable for length to be absolutely safe against index out of range
317+
msgCount := len(topics.Messages)
318+
if msgCount > 0 {
319+
if lastMsg, ok := topics.Messages[msgCount-1].AsNotEmpty(); ok {
320+
offsetID, offsetDate = lastMsg.GetID(), lastMsg.GetDate()
321+
} else {
322+
log.Debug("no valid message for offset, relying on offsetTopic only",
323+
zap.Int("offset_topic", offsetTopic))
324+
}
325+
} else {
326+
log.Debug("no messages in topics response, relying on offsetTopic only",
327+
zap.Int("offset_topic", offsetTopic),
328+
zap.Int("topics_count", len(topics.Topics)))
288329
}
289330
}
290331

0 commit comments

Comments
 (0)