Skip to content

[bufiox] TailLines 行数软上限的 int64 乘法溢出——budget >= 2^61 时尾部窗口静默塌成 1 行,且实现处注释宣称的「不回绕」与实际相反 #408

Closed

Description

@zy84338719

位置

  • bufiox/tail.go:56(maxLines := int64(budget) * tailMaxLineFactor)
  • bufiox/tail.go:68(丢头循环条件 int64(len(lines)) > maxLines)

现状

实现处自带注释(bufiox/tail.go:56):

maxLines := int64(budget) * tailMaxLineFactor // int64:budget 极大时乘法不回绕

TailLines 的 godoc(bufiox/tail.go:40-48):

//   - 超预算从**头部按行丢弃**:窗口内保留行的内容字节总数 <= budget
//     (分隔符不计入),且行数 <= budget×4(软上限,防极小行无限堆积);

问题

budget 是 int;int64(budget) * tailMaxLineFactor(tailMaxLineFactor = 4)
在 budget > 2^61 时 int64 乘法回绕。

注释宣称的「int64:budget 极大时乘法不回绕」与实现相反——
转成 int64 只避免了 int 的回绕,int64 自身的乘法一样会回绕。

回绕后的 maxLines 为负或 0 ⇒ bufiox/tail.go:68 的丢头循环条件 int64(len(lines)) > maxLines 恒真 ⇒
只保留最后一行。

⇒ godoc 承诺的「行数 <= budget×4」在 budget 极大时退化为「行数 <= 1」。

证据

for _, budget := range []int{1024, 1048576, 1 << 61, 1 << 62, math.MaxInt64} {
	tail, truncated, err := bufiox.TailLines(strings.NewReader(strings.Repeat("\n", 100)), budget)
	// 统计 tail 的行数
}

实测输出原文:

[TailLines] budget=1024                 保留行数=100  truncated=false err=<nil> (输入共 100 行,期望保留 100 行)
[TailLines] budget=1048576              保留行数=100  truncated=false err=<nil> (输入共 100 行,期望保留 100 行)
[TailLines] budget=2305843009213693952  保留行数=1    truncated=true  err=<nil> (输入共 100 行,期望保留 100 行)
[TailLines] budget=4611686018427387904  保留行数=1    truncated=true  err=<nil> (输入共 100 行,期望保留 100 行)
[TailLines] budget=9223372036854775807  保留行数=1    truncated=true  err=<nil> (输入共 100 行,期望保留 100 行)
--- PASS: TestProbe_TailLines_LineBudgetOverflow (0.00s)

对照组

  • budget=1024 / budget=1MB:保留全部 100 行、truncated=false ✓ 正常路径无恙。
  • budget >= 2^61:只保留 1 行,且 truncated=true。

影响

TailLines 的用途是「给 LLM 做 CI 失败日志尾部归因」。
预算极大时本应「几乎全量保留」,实际只剩最后一行 ⇒ 归因提示里只剩一行日志,
且 truncated=true 反而让调用方以为「只是头部被省略」——失败形态被伪装成了正常截断。

触发需要 2^61 字节级的预算(现实中不会走到),但注释里那句「int64:budget 极大时乘法不回绕」本身是错的,
会把后来的维护者引向错误的安全感(正是这类注释存在的原因才要修)。

建议修复

在乘法前判上界(或做饱和乘法),与本仓 pagination 的既有做法一致——
pagination/page.go:44-52 的注释明写「乘法前判,而不是乘法后看结果是否为负」,
且 CHANGELOG.md 有专门的修复记录(「修复(pagination):偏移量溢出保护只兜住一半情形 ⇒ 返回垃圾偏移量」)。

maxLines := int64(math.MaxInt64 / tailMaxLineFactor)
if int64(budget) < maxLines {
	maxLines = int64(budget) * tailMaxLineFactor
}

(或者用 math/bits 做饱和乘法。)

同时把那句注释改成实际成立的表述,例如:
// 乘法前判上界:budget 极大时饱和到 MaxInt64,避免 int64 乘法回绕。

版本

  • github.com/oekit/pkg HEAD 4e32ee8

Activity

  1. moved this from Todo to In Progress in oekit Issue 大盘on Nov 12, 2026
  2. added a commit that references this issue on Nov 12, 2026
  3. moved this from In Progress to Done in oekit Issue 大盘on Nov 12, 2026
  4. zy84338719 commented on Nov 12, 2026

    @zy84338719
    ContributorAuthor

    已合入修复 ✅(无 SemVer 发布:本次改动不发版本)

    变更内容

    TailLines 行数软上限乘法前判上界(同 pagination 口径),饱和 MaxInt64,修正反向注释

    合入:PR #421(https://github.com/oekit/pkg/pull/421),merge commit 63917515,2026-11-12T17:10:03Z

    CI:link ✅ / Lint & Test / Test ✅ / Lint & Test / Lint ✅ / Zero Third-Party Deps ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions