include/com/sun/star/uno/Sequence.h            |    2 ++
 include/com/sun/star/uno/Sequence.hxx          |    2 ++
 oox/source/drawingml/customshapeproperties.cxx |   15 +++++++++++----
 3 files changed, 15 insertions(+), 4 deletions(-)

New commits:
commit fb3c04bd1930eedacd406874e1a285d62bbf27d9
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Fri Oct 29 10:30:22 2021 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Fri Nov 5 12:30:28 2021 +0100

    Drop non-const Sequence::operator[] in internal code
    
    This makes all non-const operations on Sequence explicit, to avoid
    repeated COW checks in loops. Generally it would be desirable to
    replace uses of Sequence with general-purpose containers wherever
    possible, and only use Sequence for UNO API calls.
    
    This change uncovered a very serious pre-existing problem inherent
    to the Sequences used e.g. in range-based loops in our code: taking
    a non-const reference to elements of a sequence, and then modifying
    them at some later stage, brings a danger to also modify copies of
    the Sequence, that were created between the points of taking the
    reference and modifying it. This caused the change to
    oox/source/drawingml/customshapeproperties.cxx, where
    CustomShapeProperties::pushToPropSet took begin()/end() non-const
    iterators to aGeoPropSeq at the start of the loop, and then in the
    loop modified its elements and copied the Sequence passing it to
    XPropertySet::setPropertyValue. This was the same also prior to
    2484de6728bd11bb7949003d112f1ece2223c7a1, and only happened to not
    cause problems because of *accidental* use of non-const operator[]
    on the copy of the Sequence inside SdrCustomShapeGeometryItem ctor,
    which *inadvertently* triggered COW, detaching the two copies one
    from another.
    This only emphasizes that we should minimize use of Sequences in
    the codebase. I anticipate other similar problems uncovered by this
    change, that happened to not break existing unit tests.
    
    Change-Id: Id691d994a06eb14297c487ebb84d8e062e29fd47
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123725
    Tested-by: Mike Kaganski <mike.kagan...@collabora.com>
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/include/com/sun/star/uno/Sequence.h 
b/include/com/sun/star/uno/Sequence.h
index cc2c515f1322..b557ec87b4ca 100644
--- a/include/com/sun/star/uno/Sequence.h
+++ b/include/com/sun/star/uno/Sequence.h
@@ -217,6 +217,7 @@ public:
     */
     inline E const * end() const;
 
+#if !defined LIBO_INTERNAL_ONLY
     /** Non-const index operator: Obtains a reference to element indexed at
         given position.
         The implementation does not check for array bounds!
@@ -228,6 +229,7 @@ public:
         @return non-const C++ reference to element
     */
     inline E & SAL_CALL operator [] ( sal_Int32 nIndex );
+#endif
 
     /** Const index operator: Obtains a reference to element indexed at
         given position.  The implementation does not check for array bounds!
diff --git a/include/com/sun/star/uno/Sequence.hxx 
b/include/com/sun/star/uno/Sequence.hxx
index 73e51a9bc7f5..7aa873e91223 100644
--- a/include/com/sun/star/uno/Sequence.hxx
+++ b/include/com/sun/star/uno/Sequence.hxx
@@ -180,6 +180,7 @@ template<class E> E * Sequence<E>::end() { return begin() + 
getLength(); }
 template<class E> E const * Sequence<E>::end() const
 { return begin() + getLength(); }
 
+#if !defined LIBO_INTERNAL_ONLY
 template< class E >
 inline E & Sequence< E >::operator [] ( sal_Int32 nIndex )
 {
@@ -187,6 +188,7 @@ inline E & Sequence< E >::operator [] ( sal_Int32 nIndex )
     assert(nIndex >= 0 && static_cast<sal_uInt32>(nIndex) < 
static_cast<sal_uInt32>(getLength()));
     return getArray()[ nIndex ];
 }
+#endif
 
 template< class E >
 inline const E & Sequence< E >::operator [] ( sal_Int32 nIndex ) const
diff --git a/oox/source/drawingml/customshapeproperties.cxx 
b/oox/source/drawingml/customshapeproperties.cxx
index 37c42b5119dc..0aba70b7a337 100644
--- a/oox/source/drawingml/customshapeproperties.cxx
+++ b/oox/source/drawingml/customshapeproperties.cxx
@@ -182,8 +182,11 @@ void CustomShapeProperties::pushToPropSet(
             static const OUStringLiteral sType = u"Type";
             if ( aGeoPropSet >>= aGeoPropSeq )
             {
-                for ( auto& rGeoProp : asNonConstRange(aGeoPropSeq) )
+                // aGeoPropSeq gets modified in the loop, and gets copied 
elsewhere;
+                // don't use range-based for here
+                for ( sal_Int32 i = 0; i < aGeoPropSeq.getLength(); ++i )
                 {
+                    const auto& rGeoProp = aGeoPropSeq[i];
                     if ( rGeoProp.Name == sAdjustmentValues )
                     {
                         uno::Sequence< 
css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
@@ -216,16 +219,20 @@ void CustomShapeProperties::pushToPropSet(
                                     }
                                 }
                             }
-                            rGeoProp.Value <<= aAdjustmentSeq;
+                            // getArray ensures COW here - there may be copies 
of aGeoPropSeq from
+                            // prior calls to xPropSet->setPropertyValue, so 
getArray can't be
+                            // moved out of the loop:
+                            aGeoPropSeq.getArray()[i].Value <<= aAdjustmentSeq;
                             xPropSet->setPropertyValue( sCustomShapeGeometry, 
Any( aGeoPropSeq ) );
                         }
                     }
                     else if ( rGeoProp.Name == sType )
                     {
+                        // getArray ensures COW here - there may be copies of 
aGeoPropSeq:
                         if ( sConnectorShapeType.getLength() > 0 )
-                            rGeoProp.Value <<= sConnectorShapeType;
+                            aGeoPropSeq.getArray()[i].Value <<= 
sConnectorShapeType;
                         else
-                            rGeoProp.Value <<= OUString( "ooxml-CustomShape" );
+                            aGeoPropSeq.getArray()[i].Value <<= OUString( 
"ooxml-CustomShape" );
                     }
                 }
             }

Reply via email to