Static analysis: errors in media player and bugless ICQ

  • 146 views
Uploaded on

I'd like to continue our excursion of software errors and demonstration of static code analysis' utility.

I'd like to continue our excursion of software errors and demonstration of static code analysis' utility.

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
146
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
1
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Static analysis: errors in media playerand bugless ICQAuthor: Andrey KarpovDate: 18.11.2010Id like to continue our excursion of software errors and demonstration of static code analysis utility.This is my latest post about the PVS-Studio version which is not available for download yet. I think youwill be able to try the first beta-version with a new set of general-purpose rules in a week.Lets consider two projects. The first is Fennec Media Project. This is a universal media player intendedto play audio and high definition video. The source code package includes a lot of plugins and codecs butwe will analyze only the player itself. You may download the source code of the latest 1.2 Alpha versionhere.The second project is qutIM. This is a cross-platform open-source instant messaging client. We analyzedthe code available at the beginning of November, 2010. The set of source codes was provided by one ofthe developers but you may download it from the official site as well.Fennec Media Project. It is a small common project containing a common number of errors. Here is thefirst error. Or two first errors depending on how you count them. Well, the a variable is used instead ofthe b variable in two places.int fennec_tag_item_compare(struct fennec_audiotag_item *a, struct fennec_audiotag_item *b){ int v; if(a->tsize && a->tsize) v = abs(str_cmp(a->tdata, a->tdata)); else v = 1; return v;}PVS-Studio pointed to this code since the "a->tsize && a->tsize" condition is obviously suspicious.This is the diagnostic message itself and errors location in code:V501 There are identical sub-expressions to the left and to the right of the && operator: a -> tsize && a-> tsize media library.c 1076
  • 2. And here is an issue near and dear to every programmer - unnecessary semicolons. This is the firstfragment:int settings_default(void){ ... for(i=0; i<16; i++); for(j=0; j<32; j++) { settings.conversion.equalizer_bands.boost[i][j] = 0.0; settings.conversion.equalizer_bands.preamp[i] = 0.0; }}This is the PVS-Studios message and errors location in code:V529 Odd semicolon ; after for operator. settings.c 483The second fragment:int trans_rest(transcoder_settings *trans){ ... for(i=0; i<16; i++); { trans->eq.eq.preamp[i] = 0.0; for(j=0; j<32; j++) { trans->eq.eq.boost[i][j] = 0.0; } }}The PVS-Studios message and errors location in code:V529 Odd semicolon ; after for operator. settings.c 913
  • 3. There are also two other fragments with ; but I will not dwell upon them. Everything is similar anduninteresting.The issue I want to show further is not quite an error but just about. It is the CreateThread functionwhich is used instead of _beginthreadex. There are several calls of CreateThread in Fennec but I will citeonly one sample:t_sys_thread_handle sys_thread_call(t_sys_thread_function cfunc){ unsigned long tpr = 0; unsigned long tid = 0; return (t_sys_thread_handle) CreateThread(0, 0, cfunc, &tpr, 0,&tid);}The PVS-Studios warning and errors location in code:V513 Use _beginthreadex/_endthreadex functions instead of CreateThread/ExitThread functions.system.c 331I will not go into the depths explaining why you should use _beginthreadex/_endthreadex instead ofCreateThread/ExitThread. I will explain it in brief while you may read more about it here, here and here.It is said in the Scripture (i.e. in MSDN):A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and_endthreadex functions for thread management rather than CreateThread and ExitThread; this requiresthe use of the multi-threaded version of the CRT. If a thread created using CreateThread calls the CRT,the CRT may terminate the process in low-memory conditions.So, youd better secure yourself and always call _beginthreadex/_endthreadex. By the way, JeffreyRichter recommends to do the same thing in the sixth chapter of "Advanced Windows: Win32-softwaredevelopment considering the specifics of 64-bit Windows" / Translated from English, 4th issue.We also detected some poor cases of using the memset function. By the way, I have thought untilrecently that the anxiety about using memset, memcmp and memcpy is a thing of the past. They sayprogrammers wrote code with them earlier but now everybody is aware of their danger and is carefulusing these functions - they rather use sizeof(), STL-containers and so on. And everything is calm andquiet. Well, no. During the last month, I encountered so many howlers with these functions that I maysay such errors are still alive and vivid.But lets return to Fennec. Here is the first memset:#define uinput_size 1024typedef wchar_t letter;letter uinput_text[uinput_size];string basewindows_getuserinput(const string title,
  • 4. const string cap, const string dtxt){ memset(uinput_text, 0, uinput_size); ...}The PVS-Studios warning and errors location in code:V512 A call of the memset function will lead to a buffer overflow or underflow. base windows.c 151At first sight, everything is ok with "memset(uinput_text, 0, uinput_size);". Perhaps everything would beok in those times when the letter type was the char type. But now it is wchar_t, so this code clearsonly half of the buffer.Here is the second poor memset:typedef wchar_t letter;letter name[30];int Conv_EqualizerProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam){ ... memset(eqp.name, 0, 30); ...}Magic numbers are indeed evil. It does not look too much difficult to write "sizeof(eqp.name)" but stillwe are not writing it again and again and shoot off our own legs :).The PVS-Studios warning and errors location in code:V512 A call of the memset function will lead to a buffer overflow or underflow. base windows.c 2892There is also one more place with this error:V512 A call of the memset function will lead to a buffer overflow or underflow. transcode settings.c588Perhaps you noticed when working with some programs that file open/save dialogues behavedstrangely or there was some nonsense in the fields of available extensions. Now you will learn thereasons for those strange things.
  • 5. There are structures in Windows API where string-pointers must end with double zero. The most widelyused member is the lpstrFilter member in the OPENFILENAME structure. This parameter actually refersto a chain of strings separated by 0 character. It is to know that the strings have ended that we needthose two zeroes at the end.However, one might easily forget about it. Consider this code fragment:int JoiningProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam){ ... OPENFILENAME lofn; memset(&lofn, 0, sizeof(lofn)); ... lofn.lpstrFilter = uni("All Files (*.*)0*.*"); ...}The PVS-Studios message and errors location in code:V540 Member lpstrFilter should point to string terminated by two 0 characters. base windows.c 5309Whether the dialogue will work well or not depends upon what follows the string "All Files (*.*)0*.*" inmemory. The correct code must look this way: "All Files (*.*)0*.*0". We wrote one zero manuallywhile the compiler will add one more zero.There is a similar problem with other dialogues too.int callback_presets_dialog(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ ... // SAVE OPENFILENAME lofn; memset(&lofn, 0, sizeof(lofn)); ... lofn.lpstrFilter = uni("Equalizer Preset (*.feq)0*.feq"); ...
  • 6. ... // LOAD ... lofn.lpstrFilter = uni("Equalizer Preset (*.feq)0*.feq"); ...}int localsf_show_save_playlist(void){ OPENFILENAME lofn; memset(&lofn, 0, sizeof(lofn)); ... lofn.lpstrFilter = uni("Text file (*.txt)0*.txt0M3U file0*.m3u"); ...}The PVS-Studios warning messages and errors location in code:V540 Member lpstrFilter should point to string terminated by two 0 characters. base windows.c 986V540 Member lpstrFilter should point to string terminated by two 0 characters. base windows.c 1039V540 Member lpstrFilter should point to string terminated by two 0 characters. shared functions.c360And now look at a very suspicious function. However, I do not know if there is actually an error or it isjust a poorly written code:unsigned long ml_cache_getcurrent_item(void){ if(!mode_ml) return skin.shared->audio.output.playlist.getcurrentindex(); else return skin.shared->audio.output.playlist.getcurrentindex();}The PVS-Studios warning and errors location in code:V523 The then statement is equivalent to the else statement. media library window.c 430
  • 7. I did not want to analyze various plugins shipped together with Fennec, but there are as many poorfragments. I will only give a couple of samples. This is a code fragment from the project Codec ACC.void MP4RtpHintTrack::GetPayload(...){ ... if (pSlash != NULL) { pSlash++; if (pSlash != 0) { length = strlen(pRtpMap) - (pSlash - pRtpMap); *ppEncodingParams = (char *)MP4Calloc(length + 1); strncpy(*ppEncodingParams, pSlash, length); }}As it is written in the PVS-Studios diagnostic message:V528 It is odd that pointer to char type is compared with the 0 value. Probably meant: *pSlash !=0. rtphint.cpp 346,Developers forgot to dereference the pointer here. It turns out that we have a meaningless comparisonof the pointer to 0. The code must look this way: "if (*pSlash != 0)".This is a code fragment from the project Decoder Mpeg Audio:void* tag_write_setframe(char *tmem, const char *tid, const string dstr){ ... if(lset) { fhead[11] = 0; fhead[12] = 0; fhead[13] = 0; fhead[13] = 0; } ...
  • 8. }The PVS-Studios message and errors location in code:V525 The code containing the collection of similar blocks. Check items 11, 12, 13, 13 in lines 716,717, 718, 719. id3 editor.c 716Here it is - the evil Copy-Paste method :).On the whole, the general-purpose analysis in PVS-Studio demonstrated good capabilities by theexample of the Fennec Media Project project. The percentage of false alarms accompanying the analysiswas rather low. Altogether, PVS-Studio pointed to 31 code fragments, 19 fragments out of them actuallyneeded to be fixed.Now lets turn to the qutIM project.PVS-Studio failed with it. It did not find errors in it despite a rather large size of the project (about 200thousand lines), although there are certainly some. There are errors always and everywhere :). Thedevelopers of qutIM do not argue about that because qutIM does crash sometimes.So we have to give one score to the "error team".What does it mean? It means that:1) The qutIM project is a very quality product. Although it contains errors, they are rather few and are ofa level too high for static analysis (at least for PVS-Studio).2) A long way of progress and learning higher-level diagnoses lies ahead of PVS-Studio. Now it is clearerto us what we must strive for. Our purpose is to find a couple of real errors in qutIM.Did PVS-Studio generate some messages for the qutIM project? Yes, it did. But they were few and mostof them were false alarms. Among all of them, we can single out only the following things which are ofsome interest.A) CreateThread functions are used.B) We found some strange functions. One of the qutIMs authors told us later that these had been stabsthe authors had forgotten to remove. What is strange about them is that one has the name save() andthe other has the name cancel() but their contents are the same:void XSettingsWindow::save(){ QWidget *c = p->stackedWidget->currentWidget(); while (p->modifiedWidgets.count()) { SettingsWidget *widget = p->modifiedWidgets.takeFirst(); widget->save();
  • 9. if (widget != c) widget->deleteLater(); } p->buttonBox->close();}void XSettingsWindow::cancel(){ QWidget *c = p->stackedWidget->currentWidget(); while (p->modifiedWidgets.count()) { SettingsWidget *widget = p->modifiedWidgets.takeFirst(); widget->save(); if (widget != c) widget->deleteLater(); } p->buttonBox->close();}The PVS-Studios warning:V524 It is odd that the cancel function is fully equivalent to the save function (xsettingswindow.cpp,line 256). xsettingswindow.cpp 268I hope you found this post interesting and would like to try PVS-Studio 4.00 Beta soon. Of course, PVS-Studio finds few general errors at present but it is only the beginning. Besides, fixing even one singleerror at the stage of coding might let you save a lot of customers, testers and programmers nerves.