Closed
[Bug-Report|缺陷反馈]: BroadcastTo 图模式 infershape 未校验 shape 取值的负值,传 -2 仍返回 SUCCESS 且输出退化为 rank-0 #3852
liuhanwen8Add on 18 days agoClosed at 10 days ago
18 days ago Added label: bug-report
xujiachen8
18 days ago Comments:
18 days ago Comments:
您好,感谢反馈!
问题分析
核查 cann/ops-math 的 e7b7a351 后,相关 infershape 逻辑与您给出的 efff3523 一致,确实缺少对目标 shape 常量中非法负维度的检查。需要区分两个入口:_shape_values 属性路径 直接设置输出各维并返回 GRAPH_SUCCESS;shape Tensor 路径 虽有 dtype、目标 rank 和广播兼容性检查,但也没有显式拒绝 < -1 的目标维度。
对于您给出的 x=(1,)、shape=[-2],Tensor 路径中的广播检查因输入维度为 1 而放行,-2 也不会进入只处理 -1 的分支,因此仍会返回成功。您观察到的后续输出退化为 rank-0 是该环境的实测表现;从本仓源码可确认的是非法目标值没有在 infershape 阶段被拦截,rank-0 的具体形成位置还涉及 GE 形状转换。
处理方向
一个可行的解决方案是在写入输出形状前,对两个来源的实际目标维度增加一致的合法性检查,发现非法负值时返回 GRAPH_FAILED,避免先写入异常形状再成功返回。处理时需要保留 [0] 这类合法空 Tensor 广播,以及 ResolveBroadcastDim 已支持的 -1 解析规则:对齐输入的维度沿用输入大小,新增前导维度解析为 1;同时区分目标常量与框架内部的动态形状表示。验证可围绕 int32/int64、属性和 Tensor 两条路径、非法负值及现有 0/-1 行为展开。我们会持续跟踪这一问题。


17 days ago Assign chen_0715 as responsible person
17 days ago Added label: pending-confirmation
15 days ago Removed label: pending-confirmation
12 days ago Assign m0_55003149 as responsible person
12 days ago Removed responsible person chen_0715
11 days ago Associated pull request
10 days ago Close issue
10 days ago Added label: resolved
问题描述
BroadcastTo的图模式 infershape 直接把shape输入的值写进输出维度,对取值没有任何符号/范围校验:// conversion/broadcast_to/op_host/broadcast_to_infershape.cpp:44-46 const size_t dim_num = shape_attr->GetSize(); out_shape->SetDimNum(dim_num); for (size_t i = 0; i < dim_num; ++i) { out_shape->SetDim(i, shape_value[i]); // 原样写入,未判 <0 / 非法 }shape传入非法负值(-2,注意-1才是"未知维"的合法取值)时,接口仍返回GRAPH_SUCCESS,而输出形状被退化成 rank-0(dims=0)——既不报错,也不是调用方要的形状。实测(GE 图模式 host 侧 shape 推导,不跑 kernel;
x = (1,),shape为 Const 输入):shape取值y[4](对照,合法)(4)✓[-2](非法负值)[0](空 tensor,PyTorch 允许 1→0 广播)(0)即:非法负值既没有被拒绝,也没有得到正确的报错,而是静默产出退化形状。
环境信息
cann/ops-mathmasterefff3523(2026-10-23 09:14)复现脚本
// 编译/运行($ASCEND_HOME_PATH 为 CANN 安装路径;算子原型头取自本仓 op_graph, // op_const_def_math.h 在 common/inc/op_host) // g++ -std=c++17 -O2 geir_infer_bcast.cpp -o geir_infer_bcast \ // -I$ASCEND_HOME_PATH/aarch64-linux/include -I$ASCEND_HOME_PATH/aarch64-linux/include/external \ // -I$ASCEND_HOME_PATH/aarch64-linux/include/graph -I$ASCEND_HOME_PATH/aarch64-linux/include/ge \ // -I$ASCEND_HOME_PATH/opp/built-in/op_graph/inc -I$ASCEND_HOME_PATH/include \ // -I<repo>/common/inc/op_host -I<repo>/conversion/broadcast_to/op_graph \ // -L$ASCEND_HOME_PATH/aarch64-linux/lib64 -L$ASCEND_HOME_PATH/lib64 \ // -lge_runner -lge_compiler -lgraph_base -lgraph -lge_common -lge_common_base -lascendcl #include <cstdio> #include <vector> #include <map> #include <cstring> #include "graph.h" #include "types.h" #include "tensor.h" #include "ge_api.h" #include "ge_utils.h" #include "array_ops.h" #include "acl/acl.h" #include "broadcast_to_proto.h" using namespace ge; static void show(const char *tag, const Operator &n, uint32_t idx) { TensorDesc d = n.GetOutputDesc(idx); auto s = d.GetShape(); printf("%s: dims=%zu [", tag, s.GetDimNum()); for (size_t i = 0; i < s.GetDimNum(); ++i) printf("%ld%s", (long)s.GetDim(i), i + 1 < s.GetDimNum() ? "," : ""); printf("]\n"); } static Status runCase(const char *tag, int32_t targetDim) { printf("\n--- %s: x(1,) target shape=[%d] ---\n", tag, targetDim); TensorDesc dX(Shape({1}), FORMAT_ND, DT_FLOAT); TensorDesc dS(Shape({1}), FORMAT_ND, DT_INT32); auto x = op::Data("x").set_attr_index(0); x.update_output_desc_y(dX); Tensor sTensor(dS); sTensor.SetData(reinterpret_cast<const uint8_t *>(&targetDim), sizeof(targetDim)); auto s = op::Const("shape"); s.set_attr_value(sTensor); s.update_output_desc_y(dS); auto op1 = op::BroadcastTo("bt"); op1.set_input_x(x).set_input_shape(s); op1.update_input_desc_x(dX); op1.update_input_desc_shape(dS); Graph g("g"); g.AddOp(x); g.AddOp(s); g.AddOp(op1); g.SetInputs({x}); g.SetOutputs({op1}); std::vector<Shape> ins = {Shape({1})}; Status st = GeUtils::InferShape(g, ins); printf(" InferShape = %d %s\n", (int)st, (st == SUCCESS) ? "(SUCCESS)" : "(FAILED)"); if (st == SUCCESS) show(" y", op1, 0); return st; } int main() { aclInit(nullptr); aclrtSetDevice(0); std::map<AscendString, AscendString> opts; opts.emplace(AscendString("ge.exec.deviceId"), AscendString("0")); if (GEInitialize(opts) != SUCCESS) return 1; runCase("对照:target=4(合法)", 4); runCase("BUG 探测:target=-2(非法负维)", -2); runCase("BUG 探测:target=0(零维)", 0); GEFinalize(); return 0; }实测输出:
期望行为
shape输入中出现< -1的值(或除-1之外的负值)时返回GRAPH_FAILED,与同仓Reshape/SplitV等对 shape 取值的处理口径一致;不应把非法负维原样写入输出、更不应在写入失败后仍返回SUCCESS并留下 rank-0 输出。影响
图模式下传入含非法负值的
shape(例如上层动态 shape 推导出错、或把-2当作"未知维"误用)时,BroadcastTo的 infershape 既不报错也不产生正确形状,输出退化为 rank-0,错误会延后到编译/执行阶段且难以定位;与同族算子(对 shape 值有校验)行为不一致。