Sie sind auf Seite 1von 32

SDN++

Coverity Prevent
on Symbian OS
Coverity Prevent on Symbian OS
part of the SDN++ series
1st edition, 10/08

Published by:
Symbian Software Limited
2-6 Boundary Row
Southwark
London SE1 8HP
UK
www.symbian.com

Trademarks, copyright, disclaimer


‘Symbian’, ‘Symbian OS’ and other associated Symbian marks are all trademarks of Symbian
Software Ltd. Symbian acknowledges the trademark rights of all third parties referred to in
this material. Copyright © Symbian Software Ltd 2008. All rights reserved. No part of this
material may be reproduced without the express written permission of Symbian Software Ltd.
Symbian Software Ltd makes no warranty or guarantee about the suitability or the accuracy of
the information contained in this document. The information contained in this document is for
general information purposes only and should not be used or relied upon for any other
purpose whatsoever.

Contributors:
Ben Chelf, CTO, Coverity
Andy Chou, Chief Architect, Coverity
Philippe Gabriel, Symbian Software Ltd.
Sumant Kowshik, Senior Software Engineer, Coverity
Jim Shissler, Director, Coverity

Managing Editor:
Ashlee Godwin

Reviewers:
Philippe Gabriel, Symbian Software
Cathy Hoffman, Director, Business Development, Coverity
Chris Notton, Symbian Software
Jo Stichbury, Symbian Software
Mats Trovik, Symbian Software

Additional Acknowledgements:
Azita Esmaili, Symbian Software
Andy ‘Chief’ Harker, Symbian Software
Lisa Voisin, Copy Editor
Contents
Introduction .................................................................................................................................1
The value of early defect detection ............................................................................................1
Accuracy of results......................................................................................................................2
Multi-core hardware and proactive concurrency defect detection .............................................3
Coverity Prevent..........................................................................................................................3
Symbian-specific defect detection ..............................................................................................5
Implementing static analysis in Symbian environments............................................................5
Installing Coverity Prevent in Symbian environments................................................................6
Coding errors and their potential consequences on Symbian OS.............................................8
Checker: FORWARD_NULL...................................................................................................8
Checker: FORWARD_NULL (special case)............................................................................9
Checker: SYMBIAN.CLEANUP_STACK .................................................................................10
Checker: NULL_RETURNS ..................................................................................................15
Checker: EVALUATION_ORDER...........................................................................................16
Checker: DEADCODE..........................................................................................................17
Checker: REVERSE_NEGATIVE ...........................................................................................18
Checker: USE_AFTER_FREE ...............................................................................................19
Checker: OVERRUN_STATIC ...............................................................................................19
Conclusion.................................................................................................................................20
Checklist for selecting a static analysis tool ............................................................................21
Resources ..................................................................................................................................22
1

Introduction
The market for data-enabled smartphones continues to redefine the mobile device industry.
Consumers who have become accustomed to relying on PDAs and laptops now expect that
same functionality from their smartphone.

As developers, we are challenged to push the boundaries of mobile computing, and create
ever more sophisticated applications to meet the growing demands of next-generation
smartphone users. We are tasked with delivering new, easy-to-use applications that provide
increasingly sophisticated functionality. However, the path to greater end-user simplicity is
often through greater underlying complexity at the application code level. To increase the
challenge, the emergence of multi-core hardware introduces another layer of complexity for
us, since it increases the likelihood of critical software defects. Crashes, reboots and data loss
are unacceptable to customers and vendors alike, so ensuring the reliability, security and
overall integrity of our code is mandatory to ensure the success of our applications.

To ensure mobile application stability, we must find and fix critical code defects accurately
and efficiently. The traditional methods used for defect detection, QA testing and manual code
review, no longer guarantee quality or prevent field defects in today’s ever expanding, more
complex code bases. Because these testing techniques are commonly applied during the
software’s runtime execution, the number of paths through an application is severely limited.
Additionally, most testing techniques are employed late in the development cycle, when
defects are more costly and time consuming for development teams to eliminate.

To improve code quality, we recommend source code analysis to find and fix defects. An
automated tool is the best way to ensure that every possible path through the software
system is explored. Internally, Symbian uses the Coverity Prevent static analysis tool to
analyze Symbian OS, and this booklet examines the tool in detail. We explain how you can
use static source code analysis to automatically detect defects at the earliest stage in the
development cycle, to ensure the optimal quality and security of your Symbian C++
application.

In addition, this booklet presents specific examples of defects commonly found in Symbian
applications and explains how a static analysis tool can help you prevent these defects from
making it into your final release.

The value of early defect detection


Waiting until late in the development cycle to begin reviewing and improving code introduces
organizational risks and expenses that can easily be avoided. If the code base is complex,
focusing on code quality too late often results in critical defects that consume valuable QA
cycles. These defects can threaten to delay release availability, or worse, create a widespread
field defect that generates negative publicity and causes costly product repairs or recalls. By
using static analysis to detect defects at the earliest possible stage, we can significantly
increase the likelihood of resolving critical quality and security defects without delaying the
release of the product. Detecting crash-causing defects during the implementation phase costs
orders of magnitude less than finding and addressing them post-release. Static analysis can
ensure higher code quality and security than traditional code review methods while reducing
development costs and increasing speed to market.
2

Simply deploying static analysis will not help an organization increase the integrity of the
code it delivers. The tool must meet the developers’ needs by delivering an array of features
that generate high quality results. For Symbian C++ developers, the high-level features
required to ensure a successful experience with static analysis are:

• accuracy of results
• multi-core support
• Symbian-specific checking.

Accuracy of results
Many static analysis tools rely only on simple pattern matching to identify coding violations
and suspicious code constructs. While this approach is scalable for large code bases, its
analysis is inherently shallow because it fails to provide a complete understanding of the
code’s semantics. These pattern-matching tools rarely identify truly serious, crash-causing
defects. They also frequently suffer from high false-positive rates, thereby resulting in time
consuming manual defect reviews and reducing the tool’s overall value. False-positive rates
are an inherent issue with any static analysis tool due to the technology’s underlying
methodology. However, false-positive rates do vary significantly between tools. To ensure
developer adoption, a static analysis tool should deliver accurate and relevant results,
reflected in a documented false-positive rate of 20% or less.

The accuracy of a static analysis tool is related to the software system that it analyzes. The
most accurate solutions construct a complete representation of the software system. A tool
should be able to:

• Capture all operations the build system performs to understand exactly how each file is
compiled and how they are all linked.
• Create an authentic compilation of every source file in the build system to make sure the
analyzed version of that file is semantically equivalent to the output that was generated by
the compiler.
• Deliver a high-fidelity representation of the entire software system at the file, function,
statement and even expression level.

With a complete, accurate representation of the software system, an effective static analysis
tool performs deep inter-procedural analysis to uncover the defects that matter most to
developers. The term ‘inter-procedural analysis’ refers to the analysis of a whole program that
crosses the boundaries of methods or functions, thus taking into account the effects of a
function call or method invocation when analyzing the caller.

The most sophisticated static analysis can perform inter-procedural analysis with an arbitrary
call chain depth, while keeping track of context-sensitive information, such as parameter and
return values and their influence on the program’s potential flow. With this type of inter-
procedural analysis, static analysis can deliver highly accurate results. While some static
analysis solutions focus on programming style and syntax-based checks, the depth of analysis
for applications in a Symbian environment requires 100% coverage of all values and data
paths via inter-procedural analysis – not partial path coverage that can leave dangerous blind
spots in unexplored code.
3

Multi-core hardware and proactive concurrency defect


detection
To take advantage of multi-core hardware, software developers are required to create multi-
threaded applications that can execute multiple operations concurrently. However, when those
of us responsible for creating reliable mobile applications move from single- to multi-threaded
applications, we inherit this dangerous new blind spot when it comes to code quality. Multi-
threaded applications add a new level of complexity to your code, resulting in an exponential
increase in the number of possible runtime scenarios.

Static analysis tools that deliver 100% path coverage are invaluable to developers creating
multi-threaded applications. They enable us to detect crash-causing concurrency defects that
are nearly impossible to find with other techniques. Additionally, because mainstream
adoption of multi-core hardware is a relatively recent phenomenon, many of us are learning
to create multi-threaded applications on the job. For those who are unfamiliar with complex
interleavings, static detection of concurrency defects can immediately improve the quality of
our multi-threaded code. Common concurrency defects include:

• Race Conditions – Multiple threads access the same shared data without the appropriate
locks to protect access points. When this defect occurs, one thread may inadvertently
overwrite data used by another thread, leading to both loss of information and data
corruption.
• Deadlock – Two or more threads wait for a lock in a circular chain such that the locks can
never be acquired. When this defect occurs, the entire software system may halt, as none
of the threads can either proceed along their current execution paths or exit.
• Thread Block – A thread calls a long-running operation while holding a lock, thereby
preventing the progress of other threads. When this defect occurs, application performance
can drop dramatically due to a single bottleneck for all threads.

Coverity Prevent
To analyze and ensure the integrity of Symbian OS code, Symbian uses Coverity Prevent to
perform static analysis. Symbian selected Prevent because it delivers a complete view of every
operation by mapping out the full build system with its Software DNA Map analysis system. The
tool performs deep inter-procedural analysis to achieve 100% path coverage. In addition, it uses
Boolean Satisfiability (SAT) to achieve 100% value coverage. Boolean Satisfiability translates the
code into questions based on Boolean values and then applies existing SAT solvers to test for:

• variable values that can lead to error conditions


• paths that are infeasible in the code.

SAT significantly improves testing accuracy by detecting critical arithmetic and logic problems,
like integer overflows and buffer overflow.

Out of the box, Coverity Prevent features the lowest documented false-positive rate in static
code analysis — an average of below 15% — detecting only the critical bugs that matter the
most to developers.
4

Figure 1 summarizes the process of defect detection with Coverity Prevent. Further information is
available from www.coverity.com/html/prevent-for-c-c++.html.

Figure 1: Basic architecture of Coverity Prevent

Coverity Prevent detects a number of critical quality and security defects, including:

Performance degradation:
• memory leaks
• file handle leaks
• custom memory and network resource leaks
• database connection leaks.

Crash-causing defects:
• NULL pointer deference
• use after free
• double free
• improper memory allocations
• mismatched array new/delete.

Incorrect program behavior:


• dead code caused by logical errors
• uninitialized variables
• invalid use of negative variables.

Improper use of APIs:


• STL usage errors
• API error handling
• security vulnerabilities.

Secure coding defects:


• buffer overflows
• integer overflows
• missing/insufficient validation of malicious data and string input
5

• format string vulnerabilities


• SQL injection attacks
• cross-site scripting attacks.

Defect implications:
• total system compromise
• denial of service attacks
• privilege escalation
• leaking confidential data
• data loss
• arbitrary code execution.

Concurrency:
• deadlocks
• race conditions
• blocking call misuse.

Symbian-specific defect detection


Symbian OS is a unique operating system, offering developers a highly flexible, scalable
platform for delivering next-generation mobile applications. As is true for any development
platform, Symbian OS exposes certain APIs to leverage the functionality of the platform.
Developers must use these APIs correctly, or they may introduce defects into the system that
cause system crashes or data corruption. By using Symbian C++ idioms and classes, we are, to
some extent, protected from a few common coding errors such as NULL pointer dereferences,
buffer overflows, and so on. However, the misuse of Symbian OS APIs can still lead to crash-
causing defects. This requires static analysis solutions that are specifically aware of the platform
APIs, and how they should and should not be used.

Coverity Prevent contains Symbian-specific checkers designed to identify defects unique to


Symbian OS. Created with input from Symbian, these checkers allow mobile device developers
to use static analysis to help ensure the quality of their Symbian applications. For example, the
SYMBIAN.CLEANUP_STACK checker can monitor events in the code to ensure the correct usage
of the API that controls the use of pushing and popping objects onto the cleanup stack, so that
memory is not leaked or accidentally double-freed. For more information about this checker,
please see the ‘Coding errors and their potential consequences on Symbian OS’ section.

Implementing static analysis in Symbian environments


Success with static analysis requires a tool that integrates with existing development processes
and defect fixing workflows. The tool should require no changes to existing build environments
or source code to ensure smooth integration with established development pipelines. Tools that
disrupt existing processes are not popular because they exist outside established workplace
behavior and add complexity.

The most useful static tools offer the flexibility to analyze code on desktops or central build
systems and review defects anywhere through the developer’s web browser of choice. For large-
scale Symbian applications, it is also essential that the solution can scale to analyze large,
complex code bases quickly.

A static analysis tool should provide a customizable workflow through which developers can
assign, distribute and then collaborate to resolve defects. To ensure that defects can be
identified accurately and efficiently, the following capabilities are critical:
6

• Automatic Assignment of Defects – Tools that only find defects create significant
management overhead for administrators and end users. A static analysis tool should also
pinpoint which individual developer is responsible for any defect for a given code base.
Without this facility, defect assignment is time consuming and fails to provide managers
with a high-level view of the quantity of defects introduced by individual developers.
• Developer prioritization of defects – Static analysis tools should provide customizable
severity settings to identify real defects, coding violations, false-positives, real but
unimportant errors and so on. To complement existing workflow, developers should be
able to assign actions to be taken as a result of any defect discovered in the code.
• Persistent tracking of defects – One common deployment pitfall for static analysis tools is
tracking developer analysis of potential defects over time. As code churns, developers
require a static tool to track which defects are new and which have already been
diagnosed. The persistence of status across multiple analysis runs avoids the time-
consuming task of sifting through familiar results to hunt for new defects.

Lastly, to achieve greater accuracy, tuning the analysis after installation often further lowers
false-positive rates. Tuning typically involves modifying either the number of checkers
deployed or the settings specific to individual checkers. This allows static analysis to become
more precise over time as development teams learn more about how their tool comprehends
the code they create.

Installing Coverity Prevent for use in Symbian environments


Figure 2 shows the process of integrating Coverity Prevent for use in Symbian environments.
Further information is available in the Coverity ‘quick start’ user documentation.

The covbuild command can automatically detect invocations of the compiler. This method
requires no changes to the build system itself. Instead, it relies on ‘wrapping’ the build
system so that Coverity Prevent can piggyback on the compiler invocations. The regular build
system is invoked by the covbuild command, which sets up the operating environment such
that all calls to exec(unix) and CreateProcess(win32) made by the build process and
its descendant processes are intercepted by the Coverity Prevent capture stub library.

Figure 2: Installing static analysis in Symbian environments


7

After executing the covbuild command, completing the analysis of your code requires only a
few additional steps:

• Capture build commands and parameters going to native compiler.


• Every command in the build system is observed in its entirety, including all of the
environment variables and parameters passed to each executing command. Calls
to the compiler are specifically singled out, and the command line arguments that
are passed to the compiler to control compilation are recorded and translated into
equivalent command line arguments for the Coverity compiler.
• Replay build commands and parameters to Coverity Prevent integrated front end.
• With a snapshot collected of the entire build, the Coverity Prevent front end is
invoked on every source file to compile a semantically equivalent version of the
code base suitable for analysis.
• Produce an emit directory that represents the Abstract Syntax Tree (AST) of all the code
being built.
• Each call to the Coverity front end produces an AST for the compiled file as well
as other meta-data necessary for accurately analyzing the entire software system.
This information is stored for all source files in an intermediate directory called
the emit directory.
• Run cov-analyze over the emit directory.
• Once a compiled representation of the software is stored in the emit repository,
the Coverity analysis can then be run over that representation to look for defects.
• Commit errors to the Coverity Defect Manager database.
• With a complete set of them in the output directory, defects are then committed
to a central database that tracks defects discovered over time and allows end
users to retain any information they have saved about them from one analysis run
to the next.
• The Coverity Defect Manager identifies defects already found in previous analysis
and presents only those that have been newly found.
• Allow users to view and annotate defects by making the defect database accessible to
users through a web front end.
• Complete with a source code browser and all the defects discovered by the
analysis, the Coverity Prevent web front end enables defect triage.

An example of Coverity Prevent’s defect GUI is shown in Figure 3.


8

Figure 3: The defect view of Coverity Prevent

Coding errors and their potential consequences on


Symbian OS
This section describes some of the most common defects that can occur in smartphone
applications, and their potential consequences. All of the following defects can be
automatically detected by Coverity Prevent.

Checker: FORWARD_NULL
Dereferencing a NULL pointer causes a program to crash. The FORWARD_NULL checker finds
instances where a pointer is not properly checked against NULL before it is dereferenced.
Defects typically arise when the code checks for NULL but then does not properly handle the
condition, or does not check for NULL in the code path at all.
9

...
Event var_compare_op: Added "aWsAnim" due to comparison "aWsAnim"
At conditional (0): "aWsAnim" taking false path
79 if(aWsAnim && aWsAnim->iWindow)
80 {
81 aWsAnim->iWindow->Screen()->LayerROptimizer().RSched(*(aWsAnim->
iWindow));
82 }
83
Event var_deref_op: Variable "aWsAnim" tracked as NULL was
dereferenced.
84 TInt handle=aWsAnim->iAnimDll->AnimObjectHandle(aWsAnim);
85 if (handle<0)
86 delete aWsAnim;
87 else
88 aWsAnim->iAnimDll->Remove(handle);
...

Explanation of defect: In the example above, aWsAnim is compared to NULL (line 79), thereby
confirming the programmer’s belief that it could be invalid. However, later, in line 84,
iAnimDll is accessed - by dereferencing the pointer - even though it could potentially be
NULL. If aWsAnim is NULL, a KERN-EXEC 3 panic occurs (and if the code is running in a
system-critical executable, the device reboots).

Checker: FORWARD_NULL (special case: dereference occurs in a called function)


The FORWARD_NULL checker finds instances where a pointer is checked against NULL but
later passed as a parameter to another function, which dereferences it. The special case
illustrated here shows the inter-procedural analysis capabilities of Coverity Prevent, where the
erroneous dereference of the NULL pointer occurs inside a called function, that is, across
procedure boundaries.

...
509 CResolvedClient2* resolvedClient2 = NULL;
Event assign_zero: Variable "resolvedClient" assigned value 0.
510 CResolvedClient* resolvedClient = NULL;
511
At conditional (0): "aWorkerAo->Client()->PluginType() == 1" taking
false path
512 if ( aWorkerAo.Client().PluginType() == TClient::EFirstGeneration )
513 {
514 resolvedClient =
515 iCRServer.LoadPlugin1LC( aWorkerAo.Client().Uid(),
516 aWorkerAo.ResolverUid() );
517 }
518 else
519 {
520 resolvedClient2 =
521 iCRServer.LoadPlugin2LC( aWorkerAo.Client().ImplementationUid() );
522 }
523
524 TUid channelUid;
Event var_deref_model: Variable "resolvedClient" tracked as NULL was
passed to function "CRServerSession::ChannelUidL(const TClient &, TUid
10

&, CRequest *, CResolvedClient *)" that dereferences it. [details]


525 ChannelUidL( aWorkerAo.Client(), channelUid, request, resolvedClient );
...

Explanation of defect: Along the path represented in the example above, resolvedClient
is initialized to NULL and never updated.

In the Defect Manager, clicking on the ‘details’ hyperlink takes you to the
CRServerSession::ChannelUidL() method that dereferences the pointer, as shown below.
As previously described, dereferencing a NULL or invalid pointer results in a KERN-EXEC 3 panic.

...
568 void CRServerSession::ChannelUidL(const TClient& aClient,
569 TUid& aChannelUid,
570 CRequest* aRequest,
571 CResolvedClient* aResolvedClient))
...
583 {
Event deref_parm_in_call: Dereferenced parameter "aResolvedClient"
in function "CResolvedClient::ChannelL(RStringF, const TDesC8 &,
const RPointerArray<CHeaderBase> &, const TDesC8 &, const
CContentTypeHeader *)"
584 aChannelUid = aResolvedClient->ChannelL(aRequest->Method(), *uri,
aRequest->AllHeadersL(),
aRequest->Content(),
contentTypeHeader);
...

Checker: SYMBIAN.CLEANUP_STACK
Symbian OS uses leaves (which are like lightweight exceptions) for reporting errors. To handle
memory in the event of a leave, the cleanup stack is used to free outstanding memory and
avoid resource leaks.

The SYMBIAN.CLEANUP_STACK checker verifies that allocated memory always has exactly one
owner responsible for its deallocation. Particularly, in the event of a leave, the memory must
be owned by a variable in scope after the leave, or by the global cleanup stack, but not by
both. Due to interactions with the Symbian cleanup stack, this fundamental property of
ownership can be violated by a few potential defect types:

• A leaving function is called and the memory is not owned by the cleanup stack or any
variable that is in scope after the leave.
Potential defect: Allocated memory is simply leaked when its owner goes out of scope as
the result of a leave.
• Allocated memory is pushed onto the cleanup stack more than once.
Potential defect: The memory is double-freed because it has more than one owner, both of
which deallocate it.

In the Prevent 4.1 release, the checker also verifies the following two properties:
11

• Functions exit with a net addition of zero elements on the cleanup stack, or one element if
the function has an ‘LC’ suffix (this requires the multiple_pushes checker option).
• When Pop() and PopAndDestroy() function arguments are specified, the correct
argument is popped from the cleanup stack (this requires the bad_pop checker option).

Coverity’s SYMBIAN.CLEANUP_STACK checker can monitor the following events:

• alias — An object is aliased by storing a pointer in a data structure.


• alloc_fn — An allocation function.
• alloc_push_fn — Allocation function pushes allocated memory onto global cleanup
stack.
• assign — A pointer is assigned either the return value from a function that allocates
memory or a value from another pointer.
• freed_arg — Deallocation of an object.
• pop — Pop from the global cleanup stack.
• push — Push to the global cleanup stack.

By tracking these events appropriately, the checker can also determine when defects occur in
the code, such as:

• double_free — An object is freed twice or is freed while it is still on the global cleanup
stack.
• double_push — An object is pushed more than once onto the cleanup stack.
• leave_without_push — A leaving function is called without a push to global cleanup
stack.
• memory leak — Memory is not appropriately deallocated before the pointer goes out of
scope.
• bad_pop_arg — The argument to a popping function does not match argument that is
being popped from the cleanup stack (in Prevent 4.1).
• more_than_one_push — More than one allocation is pushed onto the cleanup stack
along a path in a function (in Prevent 4.1).

Developed by Coverity with input from Symbian, this checker successfully identifies critical
defects. Symbian actively uses the SYMBIAN.CLEANUP_STACK checker internally and continues
to collaborate with Coverity to further improve its accuracy and capabilities. The checker is
included in the Coverity Prevent 4.0 release so that you may use the checker in a test
environment and evaluate its results. Users that do not have this checker must enable it by
using the --symbian option to cov-analyze.

...
78 void CWapPortWatcher::DoReceiveL(const TDesC8& aRecvFrom)
79 {
80 BIOWATCHERLOG(iWatcherLog.Printf(_L("BioWap: DoReceiveL: %S, port %d"),
&iBioMsgText, iWapPort));
81
82 // Code for collecting message goes here...
83 BIOWATCHERLOG(iWatcherLog.Printf(_L8("BioWap: Recv datagram length:
%d on socket OK"), iRecvBuf->Length()));
84
85 // Create a CSmsMessage from the received buffer
Event alloc_fn: Called allocation function "CSmsBuffer::NewL()" [details]
Event assign: Assigning "CSmsBuffer::NewL()" to "buffer"
12

86 CSmsBuffer* buffer=CSmsBuffer::NewL();
Event push: Pushing "buffer" onto cleanup stack
87 CleanupStack::PushL(buffer);
88
Event alias: Argument "buffer" aliased in function
"CSmsMessage::NewL(this->iFs, N7CSmsPDU11ESmsDeliverE, buffer, 0)"
[details]
Event double_push: Object "buffer" being pushed onto cleanup stack is
already on the stack [details]
89 CSmsMessage* smsmessage = CSmsMessage::NewL(iFs, ESmsDeliver, buffer);
90 CleanupStack::PushL(smsmessage);
...

Explanation of defect: Here, buffer is first pushed on the cleanup stack in line 87.
In the Defect Manager, clicking on the ‘details’ hyperlink takes you to the
CSmsMessage::NewL() method (shown below), where the buffer is again pushed onto the
cleanup stack at line 81. If new(ELeave) CSmsMessage()at line 82 within
CSmsMessage::NewL() were to leave, this would result in a double deletion of buffer,
which in turn could result in a KERN-EXEC 3 panic. If this were to occur in a system-critical
executable, it would reboot the device.

...
Event alias: Parameter "aBuffer" might be aliased in function
""CSmsMessage::NewL(RFs &, CSmsPDU::TSmsPDUType, CSmsBufferBase *, int)""
77 EXPORT_C CSmsMessage* CSmsMessage::NewL(RFs& aFs,
CSmsPDU::TSmsPDUType aType,
CSmsBufferBase* aBuffer,
TBool aIsRPError)
78 {
79 LOGGSMU1("CSmsMessage::NewL()");
80
81 CleanupStack::PushL(aBuffer);
82 CSmsMessage* smsmessage=new(ELeave) CSmsMessage(aFs, aBuffer);
83 CleanupStack::Pop();
84 CleanupStack::PushL(smsmessage);
85 smsmessage->ConstructL(aType,aIsRPError);
86 CleanupStack::Pop();
87 return smsmessage;
88 }
...

Checker: SYMBIAN.NAMING
In the release of Coverity Prevent 4.1, the SYMBIAN.NAMING checker verifies that naming
conventions mandated by Symbian are followed properly. Failure to follow these naming
conventions can lead to serious defects in understanding the behavior of a function. In its
current form, the naming rules that the checker enforces include:

• Functions that can potentially leave must contain ‘L’ in their suffix.
• Functions that push a pointer onto the cleanup stack must contain ‘LC’ in their suffix (if an
option is enabled).
13

Coverity’s SYMBIAN.NAMING checker can monitor the following events:

• assign — A pointer is assigned a value from another pointer or from a function that
returns allocated memory.
• identity — A method returns one of its arguments.
• leave — A leaving function is called.
• pop — Pop the element off the cleanup stack.
• push — Push the element onto the cleanup stack.

By tracking these events appropriately, the checker can also determine when naming
convention violations appear in the code using the naming_error event.

This checker successfully identifies potentially critical defects. Symbian actively uses the
SYMBIAN.NAMING checker internally and continues to collaborate with Coverity to further
improve its accuracy and capabilities. The checker is included in the Coverity Prevent 4.1
release so that you may use the checker in a test environment and evaluate its results. Users
that do not have this checker must enable it using the --symbian option to cov-analyze.

...
35 void CCaptureKeys::CheckCaptureKey(const TCaptureKey& aCaptureKey)
36 {
37
38 if((aCaptureKey.iModifiers.iValue&~aCaptureKey.iModifiers.iMask)!=0)
Event leave: Leaving function "User::Leave(int)" called
39 User::Leave(KErrArgument);
40 }
41
...
73 EXPORT_C void CCaptureKeys::SetCaptureKey(TUint32 aHandle,
const TCaptureKey& aCaptureKey,
TUint8 aPriority)
74 //
75 // Finds the first capture-key from the list that matches the handle
76 // and sets it to the new value.
77 //
78 {
79
80 TCaptureKey captureKey(aCaptureKey);
81 captureKey.iKeyCodePattern.iFiller = aPriority;
Event naming_error: Leaving function CheckCaptureKey called in this
function whose name, CCaptureKeys::SetCaptureKey(unsigned long, const
TCaptureKey &, unsigned char), does not follow the naming convertion of
having an 'L' suffix [details]
82 CheckCaptureKey(captureKey);
83 TCaptureKey ck;
...

Explanation of defect: In the above example, the function SetCaptureKey() of class


CCaptureKeys calls the function CheckCaptureKey() at line 82. The SYMBIAN.NAMING
checker first analyzes CheckCaptureKey() and discovers that it may potentially leave. It
uses this information to flag a naming convention violation in SetCaptureKey(), which
ought to have an ‘L’ suffix in its name because it calls a leaving function (line 82). Indeed,
14

there is another error since CheckCaptureKey() itself violates the naming convention. It
ought to have an ‘L’ suffix too. Violating naming conventions can result in erroneous
assumptions made by callers of functions. In this case, callers of SetCaptureKey() (and
CheckCaptureKey()) are not aware that the function can leave and may not manage
memory ownership correctly in the event of an unexpected leave.

Checker: RETURN_LOCAL
The RETURN_LOCAL checker finds instances where a function returns a pointer to a local stack
variable. In C and C++, all local variables are lost upon function exit, as a stack frame is
removed and control is returned to the calling function. Variables that were allocated on the
stack inside the function are no longer relevant when it returns; their memory will be
overwritten when a new function is called. Pointers to local stack variables that are returned
to a calling function can cause memory corruption and inconsistent behavior. This checker
finds instances where a function returns a pointer to a stack-allocated variable.

...
7980 void* ptr = NULL;// Either TInetRouteInfo or TInetNeighbourInfo
7981 TTime stamp;
7982 stamp.UniversalTime();
7983
7984 if (evclass == EClassRoute)
7985 {
7986 TInetRouteInfo rinfo;
7987 aRoute->FillRouteInfo(rinfo, Elapsed(stamp));
7988 ptr = &rinfo;
7989 }
7990 else
7991 {
7992 TInetNeighbourInfo nginfo;
7993 aRoute->FillNeighbourInfo(nginfo, Elapsed(stamp));
Event local_ptr_assign_local: Assigning address of stack variable
"nginfo" to pointer "ptr"
7994 ptr = &nginfo;
Event out_of_scope: Variable "nginfo" goes out of scope
7995 }
7996
Event use_invalid: Used "ptr" pointing to out-of-scope variable
"nginfo"
7997 mgr.EventManager()->Notify(evclass, aEventType, ptr);
...

Explanation of defect: Along the path depicted in the defect above, ptr is assigned the
address of a local variable, nginfo (line 7994). However, when nginfo goes out of scope
(line 7995), its location on the stack can be used for other variables. Thus when ptr is used
in the function Notify() (line 7997), the function could access an out-of-scope or stale
location, leading to unreliable results.
15

Checker: NULL_RETURNS
The NULL_RETURNS checker finds instances of unchecked dereferences of NULL return values.
We sometimes do not test function return values, and instead use them in potentially
dangerous ways. Every time a variable is assigned the return value of a function that could
potentially return a NULL pointer, that variable must be checked against NULL before it can
be considered safe to use. Failing to check NULL pointer return values can cause crashes due
to NULL dereferencing.

...
776 // Normal case - send the On request
777 HWRMFmTxCommand::TSetFrequencyPackage pckg(iFmTxCommonData.Frequency());
Event returned_null: Function
"CHWRMPluginTransactionList::FindTransaction(unsigned char, int)"
returned NULL value (checked 12 out of 13 times) [details]
Event var_assigned: Variable "data" assigned to NULL return value from
"CHWRMPluginTransactionList::FindTransaction(unsigned char, int)"
778 THWRMPluginRequestData* data = static_cast<THWRMPluginRequestData*>
779 ( iWatcherTransactionList->FindTransaction( aTransId, EFalse ) );
780
Event dereference: Dereferencing possibly NULL "&(data)->
iRequestMessage" in call to function
"CHWRMFmTxService::ExecutePluginCommandL(const RMessage2 &,
HWRMFmTxCommand::TFmTxCmd, int, TDesC8 &)" [details]
781 TRAP( pluginErr, ExecutePluginCommandL( data->iRequestMessage,
HWRMFmTxCommand::ETxOnCmdId,
EFalse,
pckg ) );
...

Explanation of defect: In the defect report above, the function FindTransaction() is


observed to statistically check its return value against NULL in the majority of the scenarios
in which it is used (12 of 13 times). In the path depicted above however, the return value is
NOT checked against NULL, but dereferenced in the function ExecutePluginCommandL()
(line 781). This can lead to a potential NULL dereference, with consequences as described in
the 'Checker: FORWARD_NULL' section.

Checker: CHECKED_RETURN
The CHECKED_RETURN checker finds instances of inconsistencies in the way function return
values are handled. For example, it detects the case where the code neglects to handle an
error code returned from a system call. Ignoring returned function error codes and assuming
an operation's success can cause incorrect program behavior and even, in some cases,
system crashes. Here is an example defect, which is not a Symbian C++ example, but which
clearly illustrates the potential problem.

...
01 void usual_function_1() {
02 int rv = function_with_error_code();
03 if (rv == -1) {
04 handle_error():
05 }
06 }
07
16

08 void usual_function_2() {
09 int rv = function_with_error_code();
10 if (rv == -1) {
11 handle_error():
12 }
13 }
13 void unusual_function() {
Event check_return: Called function “function_with_error_code” whose
return value should be checked (Checked 2 out of 3 times
Event unchecked_value: Return value of “function_with_error_code” is
not checked
14 function_with_error_code();
15 }
...

Explanation of defect: The checker first statistically notices that in a majority of cases the
return value of the function, function_with_error_code() is checked. In the function
unusual_function(), the return value of function_with_error_code() is not saved
and checked. The effect of this defect is that a potential error condition is being ignored by
the function, which can cause erroneous behavior downstream after this function is called.

Checker: EVALUATION_ORDER
The EVALUATION_ORDER checker reports defects when it finds order-of-evaluation problems,
also known as sequence point violations. Because the C and C++ programming languages do
not specify the evaluation order of some sub expressions, you can see different behavior
when using a different compiler, compiler version, or compiler optimizer, or when running the
program on a different platform.

In the following example, the value of b after the assignment is 3 if the left side of the
operator is evaluated first, or 4 if the right side of the operator is evaluated first:

a = 1;
b = a + (a=2);

EVALUATION_ORDER looks for the following sequence points after each statement:

• comma (,)
• logical AND (&&)
• logical OR (||)
• conditional (? :)

...
966 case 2: // Up
967 {
968 TUint8* altDataPtr = (iBuffer == 1) ? &ilineDes2[1] :
&ilineDes1[1];
969
970 while (aDataPtr < aDataPtrLimit)
Event read_write_order: In "*aDataPtr++ = (TUint8)(*aDataPtr +
*altDataPtr++)", "aDataPtr" is read in "(TUint8)(*aDataPtr +
*altDataPtr++)" and written in "*aDataPtr++" (as assignment LHS) but
17

the order of evaluation is undefined because there is no intervening


sequence point.
971 {*aDataPtr++ = TUint8(*aDataPtr + *altDataPtr++);}
972 }
...

Explanation of defect: The C++ standard states that there is no sequence point in the middle
of the statement on line 971. Thus, the read of aDataPtr on the right hand side of the
assignment and the update operation to aDataPtr on the left hand side of the assignment
can occur in different orders for different compilers, or even for different versions of the same
compiler. This makes this code unreliable in the wake of upgrades and changes to the
compiler. Changes to the order of the read and the update operation can result in difficult-to-
detect logical bugs and even crashes (since it involves pointer dereferencing).

Checker: DEADCODE
The DEADCODE checker finds instances of code that can never be reached due to branches
whose condition will evaluate exactly the same each time. This checker does not warn about
function-level dead code, such as static functions that are never called.

Faulty code assumptions or other logic errors are often responsible for dead code. These
defects can have a broad range of effects. At best, dead code increases the size of source
code (and associated binaries). More seriously, logic errors can cause important code to never
execute, which can adversely affect program results or cause a program to crash.

Some dead code might be intentional. For example, defensive error checks may cause some
unreachable error paths, but they are included to guard against future changes. Also, code
that uses #if preprocessor statements — to conditionally compile different blocks for
different configurations — might have dead code in certain configurations.

Fixing these defects depends on what the code was intended to do. Removing truly dead
code eliminates the defect. For example:

...
Event const: After this line, the value of "refreshRate" is equal to
1000000
Event assignment: Assigning "1000000" to "refreshRate"
339 TInt refreshRate = 1000000;
340 _LIT(KDebugBar, "DEBUGBAR");
341 if (WsIniFile->FindVar(refreshRate, KDebugBar))
342 {
Event dead_error_condition: On this path, the condition "refreshRate <
100000" could not be true
343 if (refreshRate < 100000)
Event dead_error_line: Cannot reach this line of code, beginning
"refreshRate ..."
344 refreshRate = 50000;
345 iDebugBar = CDebugBar::NewL(this,
TRect(TSize(CurrentScreenSize().iWidth, 16)),
refreshRate);
346 }
...
18

Explanation of defect: This is a very subtle problem uncovered with dead code. This defect
uncovers the fact that refreshRate is an IN only parameter to FindVar(). The incorrect
overload of FindVar() was used, which resulted in refreshRate never being modifiable
from its default value by FindVar().

If this defect were to occur in production code, it would result in the impossibility of
changing some screen configuration options on the device.

Checker: REVERSE_NEGATIVE
During development, correctly checking the bounds of a value before using it is often
overlooked. For example, mishandling negative integers can cause hard-to-find problems -
from memory corruption to security vulnerabilities. This checker finds instances of dangerous
integer use followed by a check against NEGATIVE. Two situations could cause this scenario:

• The programmer ‘knows’ the integer cannot be negative, in which case the check is
unnecessary and should be removed as it incorrectly indicates to other programmers that
the integer could be negative.
• The integer could actually be negative, and the check needs to occur before the dangerous
use.

REVERSE_NEGATIVE can report false-positives if it incorrectly determines that:

• An integer is compared against a negative value.


• A potentially negative integer is used in a dangerous way.

Here is an example defect that is not a Symbian C++ example, but which clearly illustrates the
potential problem.

...
01 void simple_reverse_neg(int some_signed_int) {
Event negative_sink_in_call: Tracked variable “some_signed_int” used as
argument to function kmalloc
02 some_struct *x = kmalloc(some_signed_int, GFP_KERNEL);
Event check_after_sink: Variable “some_signed_int” compared to 0 after
use in a negative sink
03 if (some_signed_int < 0)
04 return error
05 }
...

Explanation of defect: In the example above, the checker notices that a signed value,
some_signed_int, is used as an argument to kmalloc(), which allocates memory (line 2).
The size argument to kmalloc()is a ‘negative sink,’ in that it should NOT be provided
negative values. Further along the path, we notice that some_signed_int is tested against
O (line 3), indicating that it could potentially be a negative value. This defect would result in
a crash within kmalloc().
19

Checker: USE_AFTER_FREE
Once memory has been freed, you cannot safely access it. The USE_AFTER_FREE checker finds
instances of freed memory use. Specifically, it finds many types of double frees and freed
pointer dereferences.

Double free defects occur when free() is called more than once with the same memory
address argument. Double freeing a pointer can result in memory free list corruptions and
crashes. Dereferencing a freed pointer is dangerous because the memory to which it points
may have been changed or may no longer be accessible.

In multi-threaded programs, double frees are especially dangerous because one thread could
allocate another's freed memory, resulting in difficult-to-track race conditions.

...
Event freed_arg: Pointer "this->iRegistry" freed by function "operator
delete (void *)"
216 delete iRegistry;
217
218 // De register Backup and Restore and cleanup memory
At conditional (0): "this->iBackupNotification" taking true path
219 if(iBackupNotification)
220 {
Event deref_arg: Dereferencing freed pointer "this->iRegistry" in call
to function "CFeatMgrFeatureRegistry::GetFeaturesFilePathAndName()"
[details]
221 TFileName temp(iRegistry->GetFeaturesFilePathAndName());
...

Explanation of defect: Along the path shown above, iRegistry is first freed (line 216) and
then dereferenced (line 221). At that point, this code could use iRegistry after it has been
freed, potentially causing memory corruption and/or a crash.

Checker: OVERRUN_STATIC
The OVERRUN_STATIC checker finds instances of overruns of constant-sized, stack-allocated
arrays.

One of the most common causes of stack corruption and security vulnerabilities is buffer
overrun. These occur when memory outside the bounds of a stack-allocated array is
manipulated, causing memory corruption. Among other things, this corruption can lead to
hard-to-locate memory inconsistencies and security holes which can allow an attacker to take
control of the system.

Buffer overruns are common because languages such as C and C++ are inherently unsafe.
Array and pointer references are not automatically bounds-checked; it is up to the
programmer to check a variable against logical bounds. This can get quite difficult when a
program's control flow involves pointers and indices passed between functions.
20

...
473 inline void SetReachableTime(TUint32 aTime)
474 /**
475 * Sets the value of reachable timer
476 * @param aTime The timer value
477 */
478 {
Event overrun-local: Overrun of static array "(*this).i" of size 8 at
position 11 with index variable "11"
479 i[11] = (TUint8)aTime;
480 i[10] = (TUint8)(aTime >> 8);
481 i[9] = (TUint8)(aTime >> 16);
482 i[8] = (TUint8)(aTime >> 24);
483 }
...

Explanation of defect: The checker detects the defect in the code above where an array of
size 8 (line 440), i, in the class, is indexed with values greater than or equal to 8 (line 479).
This can lead to memory corruption errors and security vulnerabilities due to buffer overrun.

Conclusion
Many analysts predict continued, rapid growth in the market for next-generation, data-enabled
smartphones.1 Meeting this increasing consumer demand requires a continued, rapid
innovation in mobile device application development. Smartphone functionality is becoming
more robust, even as the device form factor shifts or diminishes with hardware advances. As
this change takes place, consumers insist that their next-generation phones and applications
deliver the same reliability and security as previous generations – despite the exponential
increase in complexity facing mobile device software today, and the host of new potential
defects they pose. Symbian C++ developers recognize the importance of detecting and fixing
these critical defects to ensure the reliability and security of their code before releasing it into
the field, where errors can have a disastrous impact.

An effective method for ensuring the highest possible code integrity, static analysis enables
the automatic detection of crash-causing defects early in the development cycle, where they
are the easiest and least costly to eliminate. By optimizing code quality with static analysis,
you can increase your speed to market, reduce the development costs associated with
tedious manual code review, and prevent serious software failures that can limit the utility of
next-generation mobile devices. Ultimately, the successful implementation of static analysis
tools allows you to create more innovative, reliable mobile applications and stay at the
forefront of the dynamic Symbian OS environment.

1 Canalys Q2 2008 Statistics: mobilephonedevelopment.com/archives/657.


21

Checklist for selecting a static analysis tool


Criteria Explanation
Ease of installation A static analysis tool should not require changes to existing build
environments or source code in order to ensure smooth installation.

Accuracy False-positive rates are an unavoidable element of static analysis. To ensure


developer adoption, a false-positive rate should be under 20%.

Multi-core support The changing landscape of hardware, such as the emergence of multi-core
processors, introduces new concurrency defects in software. Look for static tools
that have the ability to detect issues such as race conditions, deadlocks and
thread blocks.

Scalability/performance ratio Many smartphone application developers have large code bases. The
analysis time required by a tool increases with the amount of code, but
effective static analysis tools should not require more than three times the
existing build time for a given code base.

Macro support A static analysis tool should work seamlessly with macros. Tools that
cannot understand macro expansions do not understand the true semantics
of the code that will ultimately be executed.

Integration with development A static analysis tool should be easy to integrate into the toolchain of all
tools, IDEs developer groups that may come to use it.

Developer prioritization of Automatic prioritization of defects is dangerous because it often ignores


defects important contexts that may surround a defect. Development organizations
should rely on the tool to identify defects, but the knowledge and
experience of their developers to prioritize them.

Custom defect detection For example, Symbian C++ can introduce slightly modified versions of
‘checkers’ common defects. A static tool should have the ability to allow developers
to create customer ‘checkers’ to identify these variant defect types. Custom
checkers can also be created to help ensure compliance with corporate or
industry coding standards.

Organizations that use an SDK for any static tool should ensure that it can
store extensions in a customer checker library so you can collaboratively
build, execute and maintain your checkers.

Automatic assignment of defects To ensure the appropriate developers are working to eliminate the defects
they are responsible for introducing, static tools should be able to send
automatic notifications as needed.

Automatic monitoring of defect To confirm that a defect discovered by a static analysis tool is fixed,
fixing development managers need a workflow that allows them to monitor its
progress.
Training and support A combination of early training and tuning can provide a springboard for
developer adoption. In many cases, a tools vendor will offer training
sessions to tune the analysis after installation, and increase accuracy.
22

Resources
Coverity Prevent Static Analysis for C/C++: www.coverity.com/html/prevent-for-c-c++.html

Symbian Developer Network: developer.symbian.com

Essential Symbian OS Coding Standards booklet:


developer.symbian.com/coding_standards_wikipage
New from

Common Design Patterns for Symbian OS:


The Foundations of Smartphone Software
Common Design Patterns for Symbian OS
introduces the common design patterns used
to implement a variety of applications and
services on Symbian OS. The book describes
patterns based on the architectural elements
of Symbian OS and discusses how patterns
suited for desktop software must be adapted
or even avoided on a mobile platform.

Multimedia on Symbian OS: Inside the Convergence Device


Combining the insights and experience of
subject experts within Symbian and the
third-party developer community, this
book will be an invaluable reference for
anyone working with multimedia on
Symbian OS. The authors provide details
of the native C++ APIs for accessing
camera, video, audio, image manipulation
and radio tuner functionality, and discuss
best practice, tips and tricks.

Symbian Press: developer.symbian.com/books


New from

Quick Recipes on Symbian OS


This book aims to make it easier to develop
applications by describing a number of
common programming tasks and providing
clear explanations of how to complete them.
The recipes are divided by technology,
including graphics, multimedia, location-
based services, networking, telephony,
connectivity and messaging.

Full sample code is available for download,


so it can be used as a starting point in your
own projects.

Games on Symbian OS: A Handbook for Mobile Development


This book forms part of the Technology
Series from Symbian Press. It describes
the key aspects of the mobile games
marketplace, with particular emphasis on
creating games for smartphones based on
Symbian OS v9.x.

It also looks at C/C++ Standards support


available for developers porting games to
Symbian OS, how to write for the N-Gage
platform, and coding for runtimes such as
Java ME and Flash Lite.
Symbian Press: developer.symbian.com/books
from

Developing Software for Symbian OS, Second Edition


This second edition of Developing Software
for Symbian OS helps software developers
new to Symbian OS to create smartphone
applications. The original book has been
updated for Symbian OS v9 and now
includes a new chapter on application
signing and platform security, and updates
throughout for Symbian OS v9 and changes
to the development environment.

Symbian OS C++ for Mobile Phones, Volume 3


The latest edition of Richard Harrison’s
existing bestsellers draws on the
experience of Symbian’s own engineers to
help you become an effective Symbian OS
developer.

If you’ve programmed in C++ at any level


and wish to develop software for Symbian
smartphones, this book gives you a
thorough grounding in writing C++
applications for Symbian OS v9.
from

For all Symbian C++ developers:


Symbian OS Communications Programming, 2nd Edition
by Iain Campbell

S60 Programming - A Tutorial Guide


by Coulton & Edwards

Symbian OS Explained
by Jo Stichbury

Symbian OS Internals
by Jane Sales

Symbian OS Platform Security


by Craig Heath

Smartphone Operating System Concepts with Symbian OS


by Mike Jipping

Accredited Symbian Developer Primer


by Jo Stichbury & Mark Jacobs
from

Published Booklets
A Guide to P.I.P.S.
Carbide.c++ v1.3
Coding Standards
Coding Tips
Creating Location-Aware Applications
Data Sharing Tips
Essential S60 - Developers’ Guide
Essential UIQ - Getting Started
Getting Started
Getting to Market
Java ME on Symbian OS
Localization
.NET Development on S60
Performance Tips
Platform Security for all
Quick Recipes Taster

Translated Booklets
Chinese Russian
Japanese Persian
Korean Italian
Spanish
Notes:
SDN++
Coverity Prevent
on Symbian OS
SDN++ Why? What? Where? How?

This booklet discusses the use of Coverity Prevent, a static


source code analysis tool. We present specific examples of
defects commonly found in Symbian C++ code and explain how
a static analysis tool such as Coverity Prevent can help you
track down these bugs and eliminate them, ensuring the
optimal quality for your application.

Coverity Prevent on Symbian OS is part of the SDN++ series,


designed to provide information in a handy format to SDN++
developers. For further information about the booklets, please
contact books@symbian.com.

Symbian Press
Symbian Press publishes books designed to communicate
authoritative, timely, relevant and practical information
about Symbian OS and related technologies. Information
about the Symbian Press series can be found at
developer.symbian.com/books

Das könnte Ihnen auch gefallen