sc/qa/unit/range.cxx            |   11 ++++++
 sc/source/core/tool/address.cxx |   63 +++++++++++++++++++---------------------
 2 files changed, 42 insertions(+), 32 deletions(-)

New commits:
commit 7bb0b11873d0c96d8116b327987834db048754c3
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Thu Feb 17 14:44:48 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Feb 17 16:59:48 2022 +0100

    fix range checking when parsing Calc cell address (tdf#147451)
    
    The document contains 'Sheet1', which Calc first tried to parse
    as a normal address, since it matches the format of e.g. 'XFD1'.
    The code parsed column into SCCOL (sal_Int16), which with 16k
    column limit overflowed and the code failed to detect the problem.
    
    Change-Id: I470db1b670dbff7bdc8013bede0a0b011e88c372
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130073
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/qa/unit/range.cxx b/sc/qa/unit/range.cxx
index d704eb2dc4e2..843ecddcb596 100644
--- a/sc/qa/unit/range.cxx
+++ b/sc/qa/unit/range.cxx
@@ -28,9 +28,11 @@ public:
 
     CPPUNIT_TEST_SUITE(ScAddressTest);
     CPPUNIT_TEST(testAddressParsing);
+    CPPUNIT_TEST(testTdf147451);
     CPPUNIT_TEST_SUITE_END();
 
     void testAddressParsing();
+    void testTdf147451();
 
 private:
     ScDocShellRef m_xDocShRef;
@@ -44,6 +46,15 @@ void ScAddressTest::testAddressParsing()
     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & 
ScRefFlags::VALID));
 }
 
+void ScAddressTest::testTdf147451()
+{
+    ScAddress aAddr;
+    ScDocument& rDoc = m_xDocShRef->GetDocument();
+    // "Sheet1" is technically a valid address like "XF1", but it should 
overflow.
+    ScRefFlags nRes = aAddr.Parse("Sheet1", rDoc, 
formula::FormulaGrammar::CONV_OOO);
+    CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & 
ScRefFlags::VALID));
+}
+
 void ScAddressTest::setUp()
 {
     BootstrapFixture::setUp();
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx
index 668c08b063d3..28f1b0b7f6ad 100644
--- a/sc/source/core/tool/address.cxx
+++ b/sc/source/core/tool/address.cxx
@@ -133,9 +133,9 @@ const sal_Unicode* parseQuotedName( const sal_Unicode* p, 
OUString& rName )
 
 }
 
-static tools::Long sal_Unicode_strtol ( const sal_Unicode*  p, const 
sal_Unicode** pEnd )
+static sal_Int64 sal_Unicode_strtol ( const sal_Unicode*  p, const 
sal_Unicode** pEnd )
 {
-    tools::Long accum = 0, prev = 0;
+    sal_Int64 accum = 0, prev = 0;
     bool is_neg = false;
 
     if( *p == '-' )
@@ -655,12 +655,13 @@ const sal_Unicode* ScRange::Parse_XL_Header(
     return p;
 }
 
-static const sal_Unicode* lcl_r1c1_get_col( const sal_Unicode* p,
+static const sal_Unicode* lcl_r1c1_get_col( const ScSheetLimits& rSheetLimits,
+                                            const sal_Unicode* p,
                                             const ScAddress::Details& rDetails,
                                             ScAddress* pAddr, ScRefFlags* 
nFlags )
 {
     const sal_Unicode *pEnd;
-    tools::Long n;
+    sal_Int64 n;
     bool isRelative;
 
     if( p[0] == '\0' )
@@ -693,7 +694,7 @@ static const sal_Unicode* lcl_r1c1_get_col( const 
sal_Unicode* p,
         n--;
     }
 
-    if( n < 0 || n >= MAXCOLCOUNT )
+    if( n < 0 || n >= rSheetLimits.GetMaxColCount())
         return nullptr;
     pAddr->SetCol( static_cast<SCCOL>( n ) );
     *nFlags |= ScRefFlags::COL_VALID;
@@ -708,7 +709,6 @@ static const sal_Unicode* lcl_r1c1_get_row(
                                     ScAddress* pAddr, ScRefFlags* nFlags )
 {
     const sal_Unicode *pEnd;
-    tools::Long n;
     bool isRelative;
 
     if( p[0] == '\0' )
@@ -718,7 +718,7 @@ static const sal_Unicode* lcl_r1c1_get_row(
     isRelative = *p == '[';
     if( isRelative )
         p++;
-    n = sal_Unicode_strtol( p, &pEnd );
+    sal_Int64 n = sal_Unicode_strtol( p, &pEnd );
     if( nullptr == pEnd )
         return nullptr;
 
@@ -821,7 +821,7 @@ static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
 
             return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
         }
-        else if( nullptr == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, 
&nFlags )))
+        else if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, 
rDetails, &r.aStart, &nFlags )))
         {
             return ScRefFlags::ZERO;
         }
@@ -830,7 +830,7 @@ static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
             (p[1] != 'R' && p[1] != 'r') ||
             nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, 
rDetails, &r.aEnd, &nFlags2 )) ||
             (*pTmp != 'C' && *pTmp != 'c') ||
-            nullptr == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, 
&nFlags2 )))
+            nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), pTmp, 
rDetails, &r.aEnd, &nFlags2 )))
         {
             // single cell reference
 
@@ -861,11 +861,11 @@ static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
     }
     else if( *p == 'C' || *p == 'c' )   // full col C#
     {
-        if( nullptr == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags 
)))
+        if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, 
rDetails, &r.aStart, &nFlags )))
             return nBailOutFlags;
 
         if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
-            nullptr == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, 
&nFlags2 )))
+            nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, 
rDetails, &r.aEnd, &nFlags2 )))
         {    // Fallback to just the initial C
             applyStartToEndFlags(nFlags);
             r.aEnd.SetCol( r.aStart.Col() );
@@ -902,8 +902,6 @@ static const sal_Unicode* lcl_a1_get_col( const ScDocument& 
rDoc,
                                                  ScRefFlags* nFlags,
                                                  const OUString* pErrRef )
 {
-    SCCOL nCol;
-
     if( *p == '$' )
     {
         *nFlags |= ScRefFlags::COL_ABS;
@@ -921,15 +919,15 @@ static const sal_Unicode* lcl_a1_get_col( const 
ScDocument& rDoc,
     if( !rtl::isAsciiAlpha( *p ) )
         return nullptr;
 
-    nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' );
+    sal_Int64 nCol = rtl::toAsciiUpperCase( *p++ ) - 'A';
     const SCCOL nMaxCol = rDoc.MaxCol();
     while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p))
-        nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + 
rtl::toAsciiUpperCase( *p++ ) - 'A' );
-    if( nCol > nMaxCol || rtl::isAsciiAlpha( *p ) )
+        nCol = ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+    if( nCol > nMaxCol || nCol < 0 || rtl::isAsciiAlpha( *p ) )
         return nullptr;
 
     *nFlags |= ScRefFlags::COL_VALID;
-    pAddr->SetCol( nCol );
+    pAddr->SetCol( sal::static_int_cast<SCCOL>( nCol ));
 
     return p;
 }
@@ -941,7 +939,6 @@ static const sal_Unicode* lcl_a1_get_row( const ScDocument& 
rDoc,
                                                  const OUString* pErrRef )
 {
     const sal_Unicode *pEnd;
-    tools::Long n;
 
     if( *p == '$' )
     {
@@ -957,12 +954,12 @@ static const sal_Unicode* lcl_a1_get_row( const 
ScDocument& rDoc,
         return p;
     }
 
-    n = sal_Unicode_strtol( p, &pEnd ) - 1;
+    sal_Int64 n = sal_Unicode_strtol( p, &pEnd ) - 1;
     if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() )
         return nullptr;
 
     *nFlags |= ScRefFlags::ROW_VALID;
-    pAddr->SetRow( static_cast<SCROW>(n) );
+    pAddr->SetRow( sal::static_int_cast<SCROW>(n) );
 
     return pEnd;
 }
@@ -1280,19 +1277,21 @@ static ScRefFlags lcl_ScAddress_Parse_OOo( const 
sal_Unicode* p, const ScDocumen
         }
         else
         {
-            const SCCOL nMaxCol = rDoc.MaxCol();
             if (rtl::isAsciiAlpha( *p ))
             {
-                nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( 
*p++ ) - 'A' );
-                while (nCol < nMaxCol && rtl::isAsciiAlpha(*p))
-                    nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + 
rtl::toAsciiUpperCase( *p++ ) - 'A' );
+                const SCCOL nMaxCol = rDoc.MaxCol();
+                sal_Int64 n = rtl::toAsciiUpperCase( *p++ ) - 'A';
+                while (n < nMaxCol && rtl::isAsciiAlpha(*p))
+                    n = ((n + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+                if (n > nMaxCol || n < 0 || (*p && *p != '$' && 
!rtl::isAsciiDigit( *p ) &&
+                        (!pErrRef || !lcl_isString( p, *pErrRef))))
+                    nBits = ScRefFlags::ZERO;
+                else
+                    nCol = sal::static_int_cast<SCCOL>( n );
             }
             else
                 nBits = ScRefFlags::ZERO;
 
-            if (nCol > nMaxCol || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) 
&&
-                        (!pErrRef || !lcl_isString( p, *pErrRef))))
-                nBits = ScRefFlags::ZERO;
             if( nBits == ScRefFlags::ZERO )
                 p = q;
         }
@@ -1327,17 +1326,17 @@ static ScRefFlags lcl_ScAddress_Parse_OOo( const 
sal_Unicode* p, const ScDocumen
             if( !rtl::isAsciiDigit( *p ) )
             {
                 nBits = ScRefFlags::ZERO;
-                nRow = SCROW(-1);
+                nRow = -1;
             }
             else
             {
-                tools::Long n = rtl_ustr_toInt32( p, 10 ) - 1;
+                sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1;
                 while (rtl::isAsciiDigit( *p ))
                     p++;
                 const SCROW nMaxRow = rDoc.MaxRow();
                 if( n < 0 || n > nMaxRow )
                     nBits = ScRefFlags::ZERO;
-                nRow = static_cast<SCROW>(n);
+                nRow = sal::static_int_cast<SCROW>(n);
             }
             if( nBits == ScRefFlags::ZERO )
                 p = q;
@@ -1795,12 +1794,12 @@ ScRefFlags ScRange::ParseCols( const ScDocument& rDoc,
 
     case formula::FormulaGrammar::CONV_XL_R1C1:
         if ((p[0] == 'C' || p[0] == 'c') &&
-            nullptr != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored 
)))
+            nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, 
rDetails, &aStart, &ignored )))
         {
             if( p[0] == ':')
             {
                 if( (p[1] == 'C' || p[1] == 'c') &&
-                    nullptr != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, 
&ignored )))
+                    nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), 
p+1, rDetails, &aEnd, &ignored )))
                 {
                     nRes = ScRefFlags::COL_VALID;
                 }

Reply via email to