From 736107f7a4fa1f9858d4da93879ca00015893628 Mon Sep 17 00:00:00 2001 From: Flavien Solt Date: Tue, 21 Apr 2026 11:10:50 +0800 Subject: [PATCH] Reject oversized step-repeat expansions --- src/gerbonara/rs274x.py | 21 ++++++++++++++------- tests/test_rs274x.py | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/gerbonara/rs274x.py b/src/gerbonara/rs274x.py index b6a9c30..cf1272d 100644 --- a/src/gerbonara/rs274x.py +++ b/src/gerbonara/rs274x.py @@ -599,6 +599,8 @@ class GerberParser: NUMBER = r"[\+-]?\d+" DECIMAL = r"[\+-]?\d+([.]?\d+)?" NAME = r"[a-zA-Z_$\.][a-zA-Z_$\.0-9+\-]+" + MAX_STEP_REPEAT_INSTANCES = 100000 + MAX_STEP_REPEAT_RESULT_OBJECTS = 100000 STATEMENT_REGEXES = { 'coord': fr"(G0?[123]|G74|G75|G54|G55)?\s*(?:X\+?(-?)({NUMBER}))?(?:Y\+?(-?)({NUMBER}))?" \ @@ -1095,18 +1097,23 @@ class GerberParser: i, j = float(match['I']), float(match['J']) if x < 1 or y < 1: raise SyntaxError('SR step-repeat X and Y values must be at least 1') + if x * y > self.MAX_STEP_REPEAT_INSTANCES: + raise SyntaxError('SR step-repeat expands to too many instances') - self.step_repeat_coords = [ - (i*nx, j*ny) - for nx in range(x) for ny in range(y)] # the order matters here, cf. the spec + self.step_repeat_coords = (x, y, i, j) self.step_repeat_objects = [] else: + x, y, i, j = self.step_repeat_coords + if len(self.step_repeat_objects) * x * y > self.MAX_STEP_REPEAT_RESULT_OBJECTS: + raise SyntaxError('SR step-repeat expands to too many objects') + for obj in self.step_repeat_objects: - for dx, dy in self.step_repeat_coords: - new_obj = copy.copy(obj) - new_obj.offset(dx, dy) - self.target.objects.append(new_obj) + for nx in range(x): + for ny in range(y): + new_obj = copy.copy(obj) + new_obj.offset(i * nx, j * ny) + self.target.objects.append(new_obj) self.step_repeat_coords = None self.step_repeat_objects = None diff --git a/tests/test_rs274x.py b/tests/test_rs274x.py index 07ca2e3..a0d7771 100644 --- a/tests/test_rs274x.py +++ b/tests/test_rs274x.py @@ -637,6 +637,23 @@ def test_syntax_error(): assert 'test_syntax_error.gbr' in exc_info.value.msg assert '7' in exc_info.value.msg # lineno +@filter_syntax_warnings +def test_step_repeat_rejects_huge_instance_counts(): + data = '\n'.join([ + 'G04 test*', + '%MOIN*%', + '%FSLAX24Y24*%', + '%ADD10C,0.0100*%', + '%SRX1000Y1000I1.0J1.0*%', + 'D10*', + 'X0000Y0000D03*', + '%SR*%', + 'M02*', + ]) + + with pytest.raises(SyntaxError, match='too many instances'): + GerberFile.from_string(data) + @filter_syntax_warnings @pytest.mark.parametrize('reference', MIN_REFERENCE_FILES, indirect=True) def test_invert_polarity(reference, tmpfile, img_support):