fix(viperlib): 浮点格式化的 inf/nan 保护失效导致 %g 死循环
根因: _check_special_float 的位双关 (把 double 位模式读成 uint64) 被 TPC 前端静默丢弃——IR 里 fbits 恒为 0 (store i64 0 后直接 load,没有把 fv 写进 fbits),函数恒返回 0,于是 %f/%e/%g/%a 的 inf/nan 保护全部失效。 后果: inf 落入 %g 的指数归一化循环 while fv>=10.0 (inf/10==inf 恒真) → 死循环,实测卡死在 vsnprintf:974(Ctrl+C 栈转储权威帧 #0)。 修复: _check_special_float 改为纯浮点语义判定 (fv!=fv 判 NaN;fv-fv!=fv-fv 判 +-inf);另给 4 处对 inf 不收敛的循环各加迭代上界 400 (正常最多约 308 次) 作纵深防御。 注: 位双关语句被静默丢弃属 TPC 前端缺陷,需另立项;va_arg(ap, double) 经生成 IR 核对是正确的 (va_arg 内建读 XMM 保存区且 gp/fp 各自推进),非本次原因。
This commit is contained in:
1 parent
b208eae8f3
commit
093ff5f66d
1 file changed
+37
-15
+37
-15
@@ -28,17 +28,23 @@ export def snprintf(buf: str, size: sizet, fmt: str, *args) -> int:
|
||||
ret: int = vsnprintf(buf, size, fmt, args)
|
||||
return ret
|
||||
|
||||
# Check floating-point special values (IEEE 754)
|
||||
# Return: 0=normal, 1=+inf, 2=-inf, 3=nan
|
||||
# 检查浮点特殊值(IEEE 754)
|
||||
# 返回:0=正常, 1=+inf, 2=-inf, 3=nan
|
||||
# 注意:不可用位双关 `*&fbits = *&fv` 读取 IEEE 位模式——该赋值会被编译器静默
|
||||
# 丢弃(IR 中 fbits 恒为 0,见 TransPyV/output/db514c1af999c5f5.text.ll),会使
|
||||
# 本函数恒返回 0、特殊值检查彻底失效,进而让 inf 进入浮点格式化循环而死循环。
|
||||
# 这里改用纯浮点语义判定,不依赖任何位操作:
|
||||
# NaN :fv != fv 为真
|
||||
# ±inf:fv - fv 得到 NaN(有限值得到 0.0)
|
||||
def _check_special_float(fv: double) -> int:
|
||||
fbits: uint64 = 0
|
||||
*&fbits = *&fv
|
||||
if ((fbits >> 52) & 0x7FF) != 0x7FF: return 0
|
||||
is_neg: int = 1 if (fbits >> 63) != 0 else 0
|
||||
if (fbits & 0x000FFFFFFFFFFFFF) == 0:
|
||||
if is_neg: return 2
|
||||
if fv != fv:
|
||||
return 3
|
||||
diff: double = fv - fv
|
||||
if diff != diff:
|
||||
if fv < 0.0:
|
||||
return 2
|
||||
return 1
|
||||
return 3
|
||||
return 0
|
||||
|
||||
export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
|
||||
if fmt is None: return 0
|
||||
@@ -626,8 +632,11 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
|
||||
di = 1
|
||||
else:
|
||||
scale: double = 1.0
|
||||
while work >= scale * 10.0:
|
||||
# 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次
|
||||
scale_guard: int = 0
|
||||
while work >= scale * 10.0 and scale_guard < 400:
|
||||
scale *= 10.0
|
||||
scale_guard += 1
|
||||
while scale >= 1.0 and di < TEMP_BUF_SIZE - 1:
|
||||
d: int = int(work / scale)
|
||||
if d > 9: d = 9
|
||||
@@ -779,12 +788,17 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
|
||||
ptr += _step
|
||||
exp10: int = 0
|
||||
if fv != 0.0:
|
||||
while fv >= 10.0:
|
||||
# 上界防护:inf 会让下面两个循环永真(inf/10==inf、inf*10==inf)
|
||||
exp_guard: int = 0
|
||||
while fv >= 10.0 and exp_guard < 400:
|
||||
fv /= 10.0
|
||||
exp10 += 1
|
||||
while fv < 1.0:
|
||||
exp_guard += 1
|
||||
exp_guard = 0
|
||||
while fv < 1.0 and exp_guard < 400:
|
||||
fv *= 10.0
|
||||
exp10 -= 1
|
||||
exp_guard += 1
|
||||
int_digit: int = int(fv)
|
||||
if int_digit > 9: int_digit = 9
|
||||
if int_digit < 0: int_digit = 0
|
||||
@@ -971,12 +985,17 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
|
||||
fv_orig: double = fv
|
||||
exp10: int = 0
|
||||
if fv != 0.0:
|
||||
while fv >= 10.0:
|
||||
# 上界防护:卡死现场即此循环(inf 会让 fv>=10.0 永真,inf/10==inf)
|
||||
exp_guard: int = 0
|
||||
while fv >= 10.0 and exp_guard < 400:
|
||||
fv /= 10.0
|
||||
exp10 += 1
|
||||
while fv < 1.0:
|
||||
exp_guard += 1
|
||||
exp_guard = 0
|
||||
while fv < 1.0 and exp_guard < 400:
|
||||
fv *= 10.0
|
||||
exp10 -= 1
|
||||
exp_guard += 1
|
||||
g_use_e: int = 0
|
||||
if exp10 < -4 or exp10 >= precision:
|
||||
g_use_e = 1
|
||||
@@ -1029,8 +1048,11 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
|
||||
g_len = 1
|
||||
else:
|
||||
scale: double = 1.0
|
||||
while work >= scale * 10.0:
|
||||
# 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次
|
||||
scale_guard: int = 0
|
||||
while work >= scale * 10.0 and scale_guard < 400:
|
||||
scale *= 10.0
|
||||
scale_guard += 1
|
||||
while scale >= 1.0 and g_len < TEMP_BUF_SIZE - 1:
|
||||
d: int = int(work / scale)
|
||||
if d > 9: d = 9
|
||||
|
||||
Reference in new issue
Block a user