Skip to content

Commit 4c62e1a

Browse files
authored
fix: avoid MulAcc overwriting constants (#1632)
1 parent e1ffd8d commit 4c62e1a

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

frontend/cs/r1cs/api.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,16 @@ func (builder *builder[E]) MulAcc(a, b, c frontend.Variable) frontend.Variable {
7777
// results fits, _a is mutated without performing a new memalloc
7878
builder.mbuf2 = builder.mbuf2[:0]
7979
builder.add([]expr.LinearExpression[E]{_a, builder.mbuf1}, false, 0, &builder.mbuf2)
80-
_a = _a[:0]
81-
if len(builder.mbuf2) <= cap(_a) {
80+
81+
// if we can add the multiplication term to the accumulator LE (by having sufficient capacity)
82+
// then we append directly into _a. However, _a can also be the hardcoded linear expressions corresponding
83+
// to zero or one constant. Now, we we would append into those then we would modify the underlying slice
84+
// thus modifying the constant themselves. This leads to undefined behaviour.
85+
//
86+
// So, in addition to only checking the capacity we also check that the underlying slices are different.
87+
// to avoid using unsafe.Pointer, we check the address of the first elements.
88+
if len(builder.mbuf2) <= cap(_a) && &(_a[0]) != &(builder.cstZero()[0]) && &(_a[0]) != &(builder.cstOne()[0]) {
89+
_a = _a[:0]
8290
// it fits, no mem alloc
8391
_a = append(_a, builder.mbuf2...)
8492
} else {

frontend/cs/r1cs/r1cs_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,36 @@ func TestSubSameNoConstraint(t *testing.T) {
171171
t.Fatal("expected 0 constraints")
172172
}
173173
}
174+
175+
type overwriteZeroConstCircuit struct {
176+
X frontend.Variable
177+
Y frontend.Variable
178+
}
179+
180+
func (c *overwriteZeroConstCircuit) Define(api frontend.API) error {
181+
constVar := api.Mul(0, c.X) // this create a zero constant
182+
constVar = api.MulAcc(constVar, c.X, c.Y) // due to bug the zero constant is overwritten
183+
_ = constVar
184+
constVar2 := api.Mul(0, c.Y) // this create another zero constant
185+
api.AssertIsEqual(constVar2, 0)
186+
187+
return nil
188+
}
189+
190+
func TestOverrideZeroConstant(t *testing.T) {
191+
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), NewBuilder, &overwriteZeroConstCircuit{})
192+
if err != nil {
193+
t.Fatal(err)
194+
}
195+
wit, err := frontend.NewWitness(&overwriteZeroConstCircuit{
196+
X: 5,
197+
Y: 10,
198+
}, ecc.BN254.ScalarField())
199+
if err != nil {
200+
t.Fatal(err)
201+
}
202+
_, err = ccs.Solve(wit)
203+
if err != nil {
204+
t.Fatal(err)
205+
}
206+
}

0 commit comments

Comments
 (0)