@@ -162,6 +162,18 @@ func (s *sqlLoader) addOperatorBundle(tx *sql.Tx, bundle *registry.Bundle) error
162162 return s .addAPIs (tx , bundle )
163163}
164164
165+ // markSeen records name in seen, erroring if it was already there. It guards
166+ // the substitutesFor chain walks below against cyclic references (e.g. a
167+ // bundle substituting for itself), which would otherwise loop until the
168+ // process runs out of memory (OCPBUGS-37284).
169+ func markSeen (seen map [string ]struct {}, name string ) error {
170+ if _ , ok := seen [name ]; ok {
171+ return fmt .Errorf ("cyclic substitutesFor chain detected involving %q" , name )
172+ }
173+ seen [name ] = struct {}{}
174+ return nil
175+ }
176+
165177func (s * sqlLoader ) addSubstitutesFor (tx * sql.Tx , bundle * registry.Bundle ) error {
166178 updateBundleReplaces , err := tx .Prepare ("update operatorbundle set replaces = ? where replaces = ?" )
167179 if err != nil {
@@ -217,11 +229,15 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
217229 if err != nil {
218230 return err
219231 }
232+ otherSeen := map [string ]struct {}{csvName : {}}
220233 for len (otherSubstitutions ) > 0 {
221234 // consume the slice of substitutions
222235 otherSubstitution := otherSubstitutions [0 ]
223236 otherSubstitutions = otherSubstitutions [1 :]
224237 if otherSubstitution != csvName {
238+ if err := markSeen (otherSeen , otherSubstitution ); err != nil {
239+ return err
240+ }
225241 // Another bundle is substituting for that same bundle
226242 // Get other bundle's version
227243 _ , _ , rawVersion , err := s .getBundleSkipsReplacesVersion (tx , otherSubstitution )
@@ -288,7 +304,11 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
288304
289305 // If the substituted-for of the current bundle substitutes for another bundle
290306 // it should also be added to the skips of the substitutesFor bundle
307+ seen := map [string ]struct {}{csvName : {}}
291308 for substitutesFor != "" {
309+ if err := markSeen (seen , substitutesFor ); err != nil {
310+ return err
311+ }
292312 skips = append (skips , substitutesFor )
293313 substitutesFor , err = s .getBundleSubstitution (tx , substitutesFor )
294314 if err != nil {
@@ -305,7 +325,11 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
305325 if err != nil || len (substitutesFors ) > 1 {
306326 return err
307327 }
328+ seen = map [string ]struct {}{csvName : {}}
308329 for len (substitutesFors ) > 0 {
330+ if err := markSeen (seen , substitutesFors [0 ]); err != nil {
331+ return err
332+ }
309333 err = s .appendSkips (tx , append (skips , csvName ), substitutesFors [0 ])
310334 if err != nil {
311335 return err
@@ -318,6 +342,7 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
318342
319343 // Bundles that skip a bundle that is substituted for
320344 // should also skip the substituted-for bundle
345+ // nolint:nestif
321346 if len (skips ) != 0 {
322347 // ensure slice of skips doesn't contain duplicates
323348 substitutesSkips := make (map [string ]struct {})
@@ -328,12 +353,16 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
328353 if err != nil || len (substitutesFors ) > 1 {
329354 return err
330355 }
356+ seen = map [string ]struct {}{skip : {}}
331357 for len (substitutesFors ) > 0 {
332358 // consume the slice of substitutions
333359 substitutesFor = substitutesFors [0 ]
334360 substitutesFors = substitutesFors [1 :]
335361 // shouldn't skip yourself
336362 if substitutesFor != csvName {
363+ if err := markSeen (seen , substitutesFor ); err != nil {
364+ return err
365+ }
337366 substitutesSkips [substitutesFor ] = struct {}{}
338367 substitutesFors , err = s .getBundlesThatSubstitutesFor (tx , substitutesFor )
339368 if err != nil || len (substitutesFors ) > 1 {
@@ -356,9 +385,13 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error
356385 if err != nil {
357386 return err
358387 }
388+ seen = map [string ]struct {}{replaces : {}}
359389 for len (substitutesFors ) > 0 {
360390 // update the replaces to a newer substitution
361391 replaces = substitutesFors [0 ]
392+ if err := markSeen (seen , replaces ); err != nil {
393+ return err
394+ }
362395 // try to get the substitution of the substitution
363396 substitutesFors , err = s .getBundlesThatSubstitutesFor (tx , replaces )
364397 if err != nil || len (substitutesFors ) > 1 {
0 commit comments