Bug 123887

Summary: Crashes with some documents on "View differences" when file has changed on disk
Product: [Applications] kate Reporter: Philippe Cloutier <chealer>
Component: generalAssignee: KWrite Developers <kwrite-bugs-null>
Status: VERIFIED FIXED    
Severity: crash    
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: Debian testing   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: Testcase

Description Philippe Cloutier 2006-03-19 11:08:09 UTC
Version:            (using KDE KDE 3.5.1)
Installed from:    Debian testing/unstable Packages
Compiler:          GCC 4 
OS:                Linux

When certain files are opened with kate/kwrite, these same files are modified by another application, and "View differences" is requested, kate/kwrite crashes reliably, without triggering the KDE Crash Handler, as reported in closed 107556/109644. The backtrace includes a loop of 6 functions which repeats at least 8000 times (at this point bt already ran since over 10 minutes and gdb monopolizes over 100 MB of VmRss, so I kill -9-d it). I'm joining the first 2 occurences of the loop. If the end of the bt can be obtained without waiting a longer time and risking to get gdb killed by oom-killer, feel free to ask me to provide more information if you can tell me how to get the bt starting from the end.

(gdb) run su-to-root.fr.1
Starting program: /usr/bin/kate su-to-root.fr.1
[Thread debugging using libthread_db enabled]
[New Thread -1234819392 (LWP 505)]
Qt: gdb: -nograb added to command-line options.
         Use the -dograb option to enforce grabbing.

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1234819392 (LWP 505)]
0xb7f2acdb in operator new () from /usr/lib/libstdc++.so.6
(gdb) bt
#0  0xb7f2acdb in operator new () from /usr/lib/libstdc++.so.6
#1  0xb6fa0e13 in QGArray::newData () from /usr/lib/libqt-mt.so.3
#2  0xb6fa0f3d in QGArray::QGArray () from /usr/lib/libqt-mt.so.3
#3  0xb6f9449e in QMemArray<char>::QMemArray () from /usr/lib/libqt-mt.so.3
#4  0xb6f926b4 in QCString::QCString () from /usr/lib/libqt-mt.so.3
#5  0xb6f927b9 in QCString::mid () from /usr/lib/libqt-mt.so.3
#6  0xb7354c70 in KProcIO::readln (this=0x8405938, line=@0xbf782140, autoAck=false, partial=0x0) at kprocio.cpp:242
#7  0xb64669ed in KateModOnHdPrompt::slotPRead (this=0xbff7fed4, p=0x8405938) at katedialogs.cpp:1659
#8  0xb6466da0 in KateModOnHdPrompt::qt_invoke (this=0xbff7fed4, _id=89, _o=0xbf7821e4) at katedialogs.moc:1217
#9  0xb6cac7ff in QObject::activate_signal () from /usr/lib/libqt-mt.so.3
#10 0xb7327492 in KProcIO::readReady (this=0x8405938, t0=0xbf7820ac) at kprocio.moc:109
#11 0xb73275da in KProcIO::controlledEmission (this=0xbf7820ac) at kprocio.cpp:209
#12 0xb732762b in KProcIO::ackRead (this=0x8405938) at kprocio.cpp:200
#13 0xb64669fb in KateModOnHdPrompt::slotPRead (this=0xbff7fed4, p=0x8405938) at katedialogs.cpp:1662
#14 0xb6466da0 in KateModOnHdPrompt::qt_invoke (this=0xbff7fed4, _id=89, _o=0xbf782324) at katedialogs.moc:1217
#15 0xb6cac7ff in QObject::activate_signal () from /usr/lib/libqt-mt.so.3
#16 0xb7327492 in KProcIO::readReady (this=0x8405938, t0=0xbf7820ac) at kprocio.moc:109
#17 0xb73275da in KProcIO::controlledEmission (this=0xbf7820ac) at kprocio.cpp:209
#18 0xb732762b in KProcIO::ackRead (this=0x8405938) at kprocio.cpp:200
#19 0xb64669fb in KateModOnHdPrompt::slotPRead (this=0xbff7fed4, p=0x8405938) at katedialogs.cpp:1662

1. Open problematic file with kate/kwrite.
2. Set encoding to UTF-8 if not already default
3. echo "t" >> su-to-root.fr.1.diffcrash
4. View differences

Results in kate/kwrite crash rather than kompare launch.

This reproduces on 2 Etch boxes (on 2 tested) when using UTF-8 encoding. It does not reproduce when using ISO 8859-1. The file I use to reproduce this is a stripped down manpage, which is much smaller than the original, but still has several lines. At this point it is becoming hard to reduce the file while keeping the crash to happen. Note that when I remove too much to continue being able to reproduce the crash, the diff shown by kompare is wrong at multi-byte characters. Those show as modified (and look as the other non-readable variant), while the only change is appending the letter "t" to the file.
Comment 1 Philippe Cloutier 2006-03-19 11:10:48 UTC
Created attachment 15191 [details]
Testcase

Stripped down version of the manpage on which I first experienced the bug.
Comment 2 Dominik Haumann 2006-04-06 14:42:50 UTC
SVN commit 526984 by dhaumann:

fix crash
CCBUG:123887


 M  +7 -2      katedialogs.cpp  


--- branches/KDE/3.4/kdelibs/kate/part/katedialogs.cpp #526983:526984
@@ -1604,7 +1604,7 @@
 
   uint lastln =  m_doc->numLines();
   for ( uint l = 0; l <  lastln; l++ )
-    p->writeStdin(  m_doc->textLine( l ), l < lastln );
+    p->writeStdin(  m_doc->textLine( l ) );
 
   p->closeWhenDone();
 }
@@ -1616,10 +1616,15 @@
     m_tmpfile = new KTempFile();
   // put all the data we have in it
   QString stmp;
+  bool readData = false;
   while ( p->readln( stmp, false ) > -1 )
+  {
     *m_tmpfile->textStream() << stmp << endl;
+    readData = true;
+  }
 
-  p->ackRead();
+  if( readData )
+    p->ackRead();
 }
 
 void KateModOnHdPrompt::slotPDone( KProcess *p )
Comment 3 Dominik Haumann 2006-04-06 14:44:14 UTC
SVN commit 526985 by dhaumann:

fix crash in View Difference
CCBUG: 123887


 M  +20 -3     katedialogs.cpp  


--- branches/KDE/3.5/kdelibs/kate/part/katedialogs.cpp #526984:526985
@@ -1634,7 +1634,7 @@
   // Start a KProcess that creates a diff
   KProcIO *p = new KProcIO();
   p->setComm( KProcess::All );
-  *p << "diff" << "-ub" << "-" <<  m_doc->url().path();
+  *p << "diff" << "-u" << "-" <<  m_doc->url().path();
   connect( p, SIGNAL(processExited(KProcess*)), this, SLOT(slotPDone(KProcess*)) );
   connect( p, SIGNAL(readReady(KProcIO*)), this, SLOT(slotPRead(KProcIO*)) );
 
@@ -1644,7 +1644,7 @@
 
   uint lastln =  m_doc->numLines();
   for ( uint l = 0; l <  lastln; l++ )
-    p->writeStdin(  m_doc->textLine( l ), l < lastln );
+    p->writeStdin( m_doc->textLine( l ) );
 
   p->closeWhenDone();
 }
@@ -1656,15 +1656,32 @@
     m_tmpfile = new KTempFile();
   // put all the data we have in it
   QString stmp;
+  bool dataRead = false;
   while ( p->readln( stmp, false ) > -1 )
+  {
     *m_tmpfile->textStream() << stmp << endl;
+    dataRead = true;
+  }
 
-  p->ackRead();
+  // dominik: only ackRead(), when we *really* read data, otherwise, this slot
+  // is called initity times, which leads to a crash
+  if( dataRead )
+    p->ackRead();
 }
 
 void KateModOnHdPrompt::slotPDone( KProcess *p )
 {
   setCursor( ArrowCursor );
+  if( ! m_tmpfile )
+  {
+    // dominik: there were only whitespace changes, so that the diff returned by
+    // diff(1) has 0 bytes. So slotPRead() is never called, as there is
+    // no data, so that m_tmpfile was never created and thus is NULL.
+    // NOTE: would be nice, if we could produce a fake-diff, so that kompare
+    //       tells us "The files are identical". Right now, we get an ugly
+    //       "Could not parse diff output".
+    m_tmpfile = new KTempFile();
+  }
   m_tmpfile->close();
 
   if ( ! p->normalExit() /*|| p->exitStatus()*/ )
Comment 4 Dominik Haumann 2006-04-06 14:52:05 UTC
SVN commit 526988 by dhaumann:

fix crash: only call KProcIO::readAck(), when we really read data.
BUG:123887


 M  +21 -3     katedialogs.cpp  


--- trunk/KDE/kdelibs/kate/part/katedialogs.cpp #526987:526988
@@ -1716,7 +1716,7 @@
 
   int lastln =  m_doc->lines();
   for ( int l = 0; l <  lastln; ++l )
-    p->writeStdin(  m_doc->line( l ), l < lastln );
+    p->writeStdin(  m_doc->line( l ) );
 
   p->closeWhenDone();
 }
@@ -1728,16 +1728,26 @@
     m_tmpfile = new KTempFile();
   // put all the data we have in it
   QString stmp;
+  bool readData = false;
   while ( p->readln( stmp, false ) > -1 )
+  {
     *m_tmpfile->textStream() << stmp << endl;
+    readData = true;
+  }
 
-  p->ackRead();
+  // dominik: only ackRead(), when we *really* read data, otherwise, this slot
+  // is called initity times, which leads to a crash
+  if( readData )
+    p->ackRead();
 }
 
 void KateModOnHdPrompt::slotPDone( KProcess *p )
 {
   setCursor( Qt::ArrowCursor );
-  m_tmpfile->close();
+  // dominik: whitespace changes lead to diff with 0 bytes, so that slotPRead is
+  // never called. Thus, m_tmpfile can be NULL
+  if( m_tmpfile )
+    m_tmpfile->close();
 
   if ( ! p->normalExit() /*|| p->exitStatus()*/ )
   {
@@ -1748,6 +1758,14 @@
     return;
   }
 
+  if ( ! m_tmpfile )
+  {
+    KMessageBox::information( this,
+                              i18n("Besides white space changes the files are identical."),
+                              i18n("Diff Output") );
+    return;
+  }
+
   KRun::runURL( m_tmpfile->name(), "text/x-diff", true );
   delete m_tmpfile;
   m_tmpfile = 0;
Comment 5 Dominik Haumann 2006-09-07 10:16:34 UTC
SVN commit 581699 by dhaumann:

Fix crashes in 'View Difference'
I wasn't aware that we duplicated the code from kate part to kate app and
thus had the same bug in the app, too.
CCBUG: 123887


 M  +16 -2     katemwmodonhddialog.cpp  


--- branches/KDE/3.5/kdebase/kate/app/katemwmodonhddialog.cpp #581698:581699
@@ -237,15 +237,29 @@
     m_tmpfile = new KTempFile();
   // put all the data we have in it
   QString stmp;
-  while ( p->readln( stmp, false ) > -1 )
+  bool dataRead = false;
+  while ( p->readln( stmp, false ) > -1 ) {
     *m_tmpfile->textStream() << stmp << endl;
+    dataRead = true;
+  }
 
-  p->ackRead();
+  if (dataRead)
+    p->ackRead();
 }
 
 void KateMwModOnHdDialog::slotPDone( KProcess *p )
 {
   setCursor( ArrowCursor );
+  if( ! m_tmpfile )
+  {
+    // dominik: there were only whitespace changes, so that the diff returned by
+    // diff(1) has 0 bytes. So slotPRead() is never called, as there is
+    // no data, so that m_tmpfile was never created and thus is NULL.
+    // NOTE: would be nice, if we could produce a fake-diff, so that kompare
+    //       tells us "The files are identical". Right now, we get an ugly
+    //       "Could not parse diff output".
+    m_tmpfile = new KTempFile();
+  }
   m_tmpfile->close();
 
   if ( ! p->normalExit() /*|| p->exitStatus()*/ )