SlideShare ist ein Scribd-Unternehmen logo
1 von 22
Downloaden Sie, um offline zu lesen
PVS-Studio delved into the FreeBSD
kernel
Author: Svyatoslav Razmyslov
Date: 17.02.2016
About a year ago we checked the Linux core. It was one of the most discussed articles at that time. We
also got quite a number of requests to check FreeBSD, so finally we decided to take the time to do it.
About the project
FreeBSD is a contemporary operating system for servers, desktops and embedded computer platforms.
Its code has gone through more than thirty years of continuous development, improvement and
optimization. It has proven itself as a system for building intranet, Internet networks, and servers. It
provides reliable network services and efficient memory management.
Despite the fact that FreeBSD is regularly checked by Coverity, we had a great time checking this project
because a lot of suspicious fragments were found. In this article we'll provide about 40 fragments, but
the developers of this project may have a look at a full list, which contains around 1000 analyzer
warnings of high severity.
To my humble opinion, a lot of those warnings issued by the analyzer are real bugs, but it's hard for me
to determine how critical they are, as I am not the developer of the system. I suppose it could be a good
ground for a discussion with the authors of the project.
The source code was taken from GitHub branch - 'master'. The repository contains ~23000 files and two
dozens of assembly configurations for different platforms, but I checked the kernel only, which I
compiled in this way:
# make buildkernel KERNCONF=MYKERNEL
Methodology
We used static code analyzer PVS-Studio, version 6.01.
For convenience, I set a PC-BSD and wrote a small utility in C++, which keeps the working environment
of the compilers' runs when building the kernel. The acquired information was used to get the
preprocessed files and their analysis, done by PVS-Studio. This method allowed me to quickly check a
project without having to study an unfamiliar build system to integrate the analyzer. On top of it,
analysis of preprocessed files allows you to do a more in-depth analysis of the code and find more
sophisticated and interesting, errors, in macros for instance. This article will provide several examples of
such a kind.
Linux kernel was analyzed in the same way; this mode is also available for Windows users in the
Standalone utility, which is a part of PVS-studio distribution kit. Usually PVS-Studio seamlessly integrates
into the projects. There is a number of ways to integrate the analyzer, described in the documentation.
Monitoring utilities have a big advantage of trying the analyzer if the project has an unusual building
system.
Surprising luck
The first possible error was found before I ran the analyzer on the project, and even before I built the
kernel; the build was interrupted by a linking error. Having addressed the file, specified in the error, I
saw the following:
Pay attention to the highlighted fragment: a tab character is used for the formatting of the indentations;
two statements are moved under the condition. But the last statement does not actually refer to a
condition and will be always executed. Perhaps, curly braces were forgotten here.
Once we got a comment that we just copy the analyzer warnings, but it is not so. Before the analysis of
the project we have to make sure that it gets compiled correctly; when the report is done, the warnings
must be sorted/examined and commented. The same work is done by our customer support team,
when they answer the incoming mails. There are also cases when the customers send examples of false
positives (in their opinion) which turn out to be real bugs.
Capy-poste and typos
PVS-Studio analyzer is a powerful tool for static code analysis that finds bugs of various levels of
severity. The first diagnostics were very simple and were created to detect most common bugs, related
to typos and copy-paste programming. After the analysis review, I sort them according to the error
code. So in this article we'll start with this type of diagnostic rules.
V501 There are identical sub-expressions '(uintptr_t) b->handler' to the left and to the right of the '>'
operator. ip_fw_sockopt.c 2893
static int
compare_sh(const void *_a, const void *_b)
{
const struct ipfw_sopt_handler *a, *b;
a = (const struct ipfw_sopt_handler *)_a;
b = (const struct ipfw_sopt_handler *)_b;
....
if ((uintptr_t)a->handler < (uintptr_t)b->handler)
return (-1);
else if ((uintptr_t)b->handler > (uintptr_t)b->handler) // <=
return (1);
return (0);
}
Here is a vivid example of a bad practice - giving the variables short and uninformative names. Now
because of the typo in the letter 'b', the a part of the condition will never be return 1. Thus, the function
returns a zero status not always correctly.
V501 There are identical sub-expressions to the left and to the right of the '!=' operator: m-
>m_pkthdr.len != m->m_pkthdr.len key.c 7208
int
key_parse(struct mbuf *m, struct socket *so)
{
....
if ((m->m_flags & M_PKTHDR) == 0 ||
m->m_pkthdr.len != m->m_pkthdr.len) { // <=
....
goto senderror;
}
....
}
One of the fields of the structure is compared with itself; therefore, the result of the logical operation
will always be False.
V501 There are identical sub-expressions to the left and to the right of the '|' operator:
PIM_NOBUSRESET | PIM_NOBUSRESET sbp_targ.c 1327
typedef enum {
PIM_EXTLUNS = 0x100,
PIM_SCANHILO = 0x80,
PIM_NOREMOVE = 0x40,
PIM_NOINITIATOR = 0x20,
PIM_NOBUSRESET = 0x10, // <=
PIM_NO_6_BYTE = 0x08,
PIM_SEQSCAN = 0x04,
PIM_UNMAPPED = 0x02,
PIM_NOSCAN = 0x01
} pi_miscflag;
static void
sbp_targ_action1(struct cam_sim *sim, union ccb *ccb)
{
....
struct ccb_pathinq *cpi = &ccb->cpi;
cpi->version_num = 1; /* XXX??? */
cpi->hba_inquiry = PI_TAG_ABLE;
cpi->target_sprt = PIT_PROCESSOR
| PIT_DISCONNECT
| PIT_TERM_IO;
cpi->transport = XPORT_SPI;
cpi->hba_misc = PIM_NOBUSRESET | PIM_NOBUSRESET; // <=
....
}
In this example we see that the same variable "PIM_NOBUSRESET" is used in the bitwise operation,
which doesn't affect the result in any way. Most likely a constant with a different value was meant to be
used here, but the variable was left unchanged.
V523 The 'then' statement is equivalent to the 'else' statement. saint.c 2023
GLOBAL void siSMPRespRcvd(....)
{
....
if (agNULL == frameHandle)
{
/* indirect mode */
/* call back with success */
(*(ossaSMPCompletedCB_t)(pRequest->completionCB))(agRoot,
pRequest->pIORequestContext, OSSA_IO_SUCCESS, payloadSize,
frameHandle);
}
else
{
/* direct mode */
/* call back with success */
(*(ossaSMPCompletedCB_t)(pRequest->completionCB))(agRoot,
pRequest->pIORequestContext, OSSA_IO_SUCCESS, payloadSize,
frameHandle);
}
....
}
Two condition branches are commented differently: /* indirect mode */ and /* direct mode */, but they
are implemented similarly, which is very suspicious.
V523 The 'then' statement is equivalent to the 'else' statement. smsat.c 2848
osGLOBAL void
smsatInquiryPage89(....)
{
....
if (oneDeviceData->satDeviceType == SATA_ATA_DEVICE)
{
pInquiry[40] = 0x01; /* LBA Low */
pInquiry[41] = 0x00; /* LBA Mid */
pInquiry[42] = 0x00; /* LBA High */
pInquiry[43] = 0x00; /* Device */
pInquiry[44] = 0x00; /* LBA Low Exp */
pInquiry[45] = 0x00; /* LBA Mid Exp */
pInquiry[46] = 0x00; /* LBA High Exp */
pInquiry[47] = 0x00; /* Reserved */
pInquiry[48] = 0x01; /* Sector Count */
pInquiry[49] = 0x00; /* Sector Count Exp */
}
else
{
pInquiry[40] = 0x01; /* LBA Low */
pInquiry[41] = 0x00; /* LBA Mid */
pInquiry[42] = 0x00; /* LBA High */
pInquiry[43] = 0x00; /* Device */
pInquiry[44] = 0x00; /* LBA Low Exp */
pInquiry[45] = 0x00; /* LBA Mid Exp */
pInquiry[46] = 0x00; /* LBA High Exp */
pInquiry[47] = 0x00; /* Reserved */
pInquiry[48] = 0x01; /* Sector Count */
pInquiry[49] = 0x00; /* Sector Count Exp */
}
....
}
This example is even more suspicious than the previous one. A big code fragment was copied, but later
no changes were made.
V547 Expression is always true. Probably the '&&' operator should be used here. qla_hw.c 799
static int
qla_tx_tso(qla_host_t *ha, struct mbuf *mp, ....)
{
....
if ((*tcp_opt != 0x01) || (*(tcp_opt + 1) != 0x01) ||
(*(tcp_opt + 2) != 0x08) || (*(tcp_opt + 2) != 10)) { // <=
return -1;
}
....
}
Here the analyzer detected that the condition "(* (tcp_opt + 2)! = 0x08) | (* (tcp_opt + 2)! = 10) "is
always true and it is really so, if you build a truth table. But most likely the '&&' is not needed here, it is
just a typo in the address offset. Perhaps the function code should be like this:
static int
qla_tx_tso(qla_host_t *ha, struct mbuf *mp, ....)
{
....
if ((*tcp_opt != 0x01) || (*(tcp_opt + 1) != 0x01) ||
(*(tcp_opt + 2) != 0x08) || (*(tcp_opt + 3) != 10)) {
return -1;
}
....
}
V571 Recurring check. This condition was already verified in line 1946. sahw.c 1949
GLOBAL
bit32 siHDAMode_V(....)
{
....
if( saRoot->memoryAllocated.agMemory[i].totalLength > biggest)
{
if(biggest < saRoot->memoryAllocated.agMemory[i].totalLength)
{
save = i;
biggest = saRoot->memoryAllocated.agMemory[i].totalLength;
}
}
....
}
This code is really strange, if we simplify it, we'll see the following:
if( A > B )
{
if (B < A)
{
....
}
}
The same condition is checked twice. Most likely, something else was supposed to be written here.
A similar fragment:
 V571 Recurring check. This condition was already verified in line 1940. if_rl.c 1941
Dangerous macros
V523 The 'then' statement is equivalent to the 'else' statement. agtiapi.c 829
if (osti_strncmp(buffer, "0x", 2) == 0)
{
maxTargets = osti_strtoul (buffer, &pLastUsedChar, 0);
AGTIAPI_PRINTK( ".... maxTargets = osti_strtoul 0 n" );
}
else
{
maxTargets = osti_strtoul (buffer, &pLastUsedChar, 10);
AGTIAPI_PRINTK( ".... maxTargets = osti_strtoul 10n" );
}
First, I skipped this analyzer warning, thinking that it is a false positive. But warnings of low severity
should also be reviewed after the project check (to improve the analyzer). So I came across such a
macro:
#define osti_strtoul(nptr, endptr, base) 
strtoul((char *)nptr, (char **)endptr, 0)
The 'base' parameter isn't used at all, and the '0' value is always passed to the "strtoul" function as the
last parameter, although values '0' and '10' are passed to the macro. In the preprocessed files all macros
got expanded and the code became similar. This macro is used in this way several dozen times. The
entire list of such fragments was sent to the developers.
V733 It is possible that macro expansion resulted in incorrect evaluation order. Check expression: chan -
1 * 20. isp.c 2301
static void
isp_fibre_init_2400(ispsoftc_t *isp)
....
if (ISP_CAP_VP0(isp))
off += ICB2400_VPINFO_PORT_OFF(chan);
else
off += ICB2400_VPINFO_PORT_OFF(chan - 1); // <=
....
}
At first glance, there is nothing strange in this code fragment. We see that sometimes the 'chan' value is
used, sometimes less by one 'chan - 1', but let's have look at the macro definition:
#define ICB2400_VPOPT_WRITE_SIZE 20
#define ICB2400_VPINFO_PORT_OFF(chan) 
(ICB2400_VPINFO_OFF + 
sizeof (isp_icb_2400_vpinfo_t) + 
(chan * ICB2400_VPOPT_WRITE_SIZE)) // <=
When passing the binary expression to the macro, the calculation logic changes dramatically. The
expression "(chan - 1) * 20" turns into "chan - 1 *20", i.e. into "chan - 20", and the incorrectly calculated
size gets used further in the program.
About the priorities of operations
In this section, I will discuss how important it is to know the priorities of operations, use extra
parentheses, if you are not sure and sometimes test yourself by building truth tables of logical
expressions.
V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a
lower priority than the '|' operator. ata-serverworks.c 166
ata_serverworks_chipinit(device_t dev)
{
....
pci_write_config(dev, 0x5a,
(pci_read_config(dev, 0x5a, 1) & ~0x40) |
(ctlr->chip->cfg1 == SWKS_100) ? 0x03 : 0x02, 1);
}
....
}
The priority of '?:' operator is lower than of the bitwise OR '|'. As a result, in the bit operations, in
addition to the numeric constants, the expression result "(ctlr-> chip > cfg1 = SWKS_100)" gets used,
which suddenly changes the calculation/computation logic. Perhaps this error wasn't noticed so far
because the result seemed so close to the truth.
V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a
lower priority than the '|' operator. in6.c 1318
void
in6_purgeaddr(struct ifaddr *ifa)
{
....
error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags |
(ia->ia_dstaddr.sin6_family == AF_INET6) ? RTF_HOST : 0);
....
}
A different file also had a fragment with a similar error with a ternary operator.
V547 Expression 'cdb[0] != 0x28 || cdb[0] != 0x2A' is always true. Probably the '&&' operator should be
used here. mfi_tbolt.c 1110
int
mfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
{
....
if (cdb[0] != 0x28 || cdb[0] != 0x2A) { // <='
if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) {
device_printf(sc->mfi_dev, "Mapping from MFI "
"to MPT Failed n");
return 1;
}
}
else
device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIOn");
....
}
The first conditional expression is always true, that's why the 'else' branch never gets control. I will
provide the truth table in case of controversial logical expressions in this and the following examples. An
example for this case:
V590 Consider inspecting the 'error == 0 || error != - 1' expression. The expression is excessive or
contains a misprint. nd6.c 2119
int
nd6_output_ifp(....)
{
....
/* Use the SEND socket */
error = send_sendso_input_hook(m, ifp, SND_OUT,
ip6len);
/* -1 == no app on SEND socket */
if (error == 0 || error != -1) // <=
return (error);
....
}
The problem with this fragment is that the conditional expression doesn't depend on the result "error
== 0". Perhaps, something is wrong here.
Three more cases:
 V590 Consider inspecting the 'error == 0 || error != 35' expression. The expression is excessive
or contains a misprint. if_ipw.c 1855
 V590 Consider inspecting the 'error == 0 || error != 27' expression. The expression is excessive
or contains a misprint. if_vmx.c 2747
 V547 Expression is always true. Probably the '&&' operator should be used here. igmp.c 1939
V590 Consider inspecting this expression. The expression is excessive or contains a misprint. sig_verify.c
94
enum uni_ieact {
UNI_IEACT_CLEAR = 0x00, /* clear call */
....
}
void
uni_mandate_epref(struct uni *uni, struct uni_ie_epref *epref)
{
....
maxact = -1;
FOREACH_ERR(e, uni) {
if (e->ie == UNI_IE_EPREF)
continue;
if (e->act == UNI_IEACT_CLEAR)
maxact = UNI_IEACT_CLEAR;
else if (e->act == UNI_IEACT_MSG_REPORT) {
if (maxact == -1 && maxact != UNI_IEACT_CLEAR) // <=
maxact = UNI_IEACT_MSG_REPORT;
} else if (e->act == UNI_IEACT_MSG_IGNORE) {
if (maxact == -1)
maxact = UNI_IEACT_MSG_IGNORE;
}
}
....
}
The result of the whole conditional expression doesn'r depend on the calculation of the value "maxact
!= UNI_IEACT_CLEAR". Here's how it looks in the table:
In this section I give three ways of how to make an error in seemingly simple formulas. Just think of it...
V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. aacraid.c 2854
#define EINVAL 22 /* Invalid argument */
#define EFAULT 14 /* Bad address */
#define EPERM 1 /* Operation not permitted */
static int
aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
{
....
int error, transfer_data = 0;
....
if ((error = copyin((void *)&user_srb->data_len, &fibsize,
sizeof (u_int32_t)) != 0))
goto out;
if (fibsize > (sc->aac_max_fib_size-sizeof(....))) {
error = EINVAL;
goto out;
}
if ((error = copyin((void *)user_srb, srbcmd, fibsize) != 0))
goto out;
....
out:
....
return(error);
}
In this function the error code gets corrupted, when the assignment is executed in the 'if' operator. I.e.
in the expression "error = copyin(...) != 0" the "copyin(...) != 0" is evaluated first, and then the result (0
or 1) is written to the variable 'error'.
The documentation for the function 'copyin' states that in case of an error, it returns EFAULT (value 14),
and after such a check, the result of a logical operation '1' gets stored in the error code. It is actually
EPERM, a completely different error status.
Unfortunately, there is quite a number of such fragments.
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. aacraid.c 2861
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_age.c 591
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_alc.c 1535
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_ale.c 606
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_jme.c 807
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_msk.c 1626
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_stge.c 511
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. hunt_filter.c 973
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_smsc.c 1365
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. if_vte.c 431
 V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as
following: 'A = (B != C)'. zfs_vfsops.c 498
Strings
V541 It is dangerous to print the string 'buffer' into itself. ata-highpoint.c 102
static int
ata_highpoint_probe(device_t dev)
{
....
char buffer[64];
....
strcpy(buffer, "HighPoint ");
strcat(buffer, idx->text);
if (idx->cfg1 == HPT_374) {
if (pci_get_function(dev) == 0)
strcat(buffer, " (channel 0+1)");
if (pci_get_function(dev) == 1)
strcat(buffer, " (channel 2+3)");
}
sprintf(buffer, "%s %s controller",
buffer, ata_mode2str(idx->max_dma));
....
}
Some string is formed in the buffer. Then the programmer wants to get a new string, saving the previous
string value, and add two more words. It seems really simple.
To explain why unexpected result will be recieved here, I will quote a simple and clear example from the
documentation for this diagnostic:
char s[100] = "test";
sprintf(s, "N = %d, S = %s", 123, s);
As a result of the work we would want to get the following string:
N = 123, S = test
But in practice it will be like this:
N = 123, S = N = 123, S =
In other situations, the same code can lead not only to the incorrect text, but also to the program
abortion. The code can be fixed if you use a new buffer to store the result . The correct version:
char s1[100] = "test";
char s2[100];
sprintf(s2, "N = %d, S = %s", 123, s1);
V512 A call of the 'strcpy' function will lead to overflow of the buffer 'p->vendor'. aacraid_cam.c 571
#define SID_VENDOR_SIZE 8
char vendor[SID_VENDOR_SIZE];
#define SID_PRODUCT_SIZE 16
char product[SID_PRODUCT_SIZE];
#define SID_REVISION_SIZE 4
char revision[SID_REVISION_SIZE];
static void
aac_container_special_command(struct cam_sim *sim, union ccb *ccb,
u_int8_t *cmdp)
{
....
/* OEM Vendor defines */
strcpy(p->vendor,"Adaptec "); // <=
strcpy(p->product,"Array "); // <=
strcpy(p->revision,"V1.0"); // <=
....
}
All three strings here are filled incorrectly. There is no space for null-terminal symbol in the arrays,
which may cause serious problems with such strings in the future. One space can be removed in "p-
>vendor" and "p->product". Then there will be room for null terminal, that the strcpy() function adds to
the end of the string. But there is no free space at all for the end of line characters for the "p->revision";
that's why the value SID_REVISION_SIZE should be increased at least by one.
Of course, it is rather hard for me to judge about the code. It's possible, that the terminal null is not
needed at all and everything is designed for a specific buffer size. Then the strcpy() function is chosen
incorrectly. In this case the code should be written like this:
memcpy(p->vendor, "Adaptec ", SID_VENDOR_SIZE);
memcpy(p->product, "Array ", SID_PRODUCT_SIZE);
memcpy(p->revision, "V1.0", SID_REVISION_SIZE);
V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value:
td->td_name. subr_turnstile.c 1029
static void
print_thread(struct thread *td, const char *prefix)
{
db_printf("%s%p (tid %d, pid %d, ....", prefix, td, td->td_tid,
td->td_proc->p_pid, td->td_name[0] != '0' ? td->td_name :
td->td_name);
}
Suspicious fragment. Despite the "td->td_name[0] != '0'" check, this string is still printed.
Here are such fragments:
 V583 The '?:' operator, regardless of its conditional expression, always returns one and the same
value: td->td_name. subr_turnstile.c 1112
 V583 The '?:' operator, regardless of its conditional expression, always returns one and the same
value: td->td_name. subr_turnstile.c 1196
Operations with memory
In this section I will tell about incorrect usage of the following functions:
void bzero(void *b, size_t len);
int copyout(const void *kaddr, void *uaddr, size_t len);
V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect
the second argument. osapi.c 316
/* Autosense storage */
struct scsi_sense_data sense_data;
void
ostiInitiatorIOCompleted(....)
{
....
bzero(&csio->sense_data, sizeof(&csio->sense_data));
....
}
To zero the structure, we should pass the structure pointer and the size of the memory to be zeroed in
bytes to the bzero() function; but here the pointer size is passed to the function, not the structure size.
The correct code should be like this:
bzero(&csio->sense_data, sizeof(csio->sense_data));
V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect
the second argument. acpi_package.c 83
int
acpi_PkgStr(...., void *dst, ....)
{
....
bzero(dst, sizeof(dst));
....
}
In this example we see a similar situation: the size of the pointer, not the object gets passed to the
'bzero' function.
Correct version:
bzero(dst, sizeof(*dst));
V579 The copyout function receives the pointer and its size as arguments. It is possibly a mistake.
Inspect the third argument. if_nxge.c 1498
int
xge_ioctl_stats(xge_lldev_t *lldev, struct ifreq *ifreqp)
{
....
*data = (*data == XGE_SET_BUFFER_MODE_1) ? 'Y':'N';
if(copyout(data, ifreqp->ifr_data, sizeof(data)) == 0) // <=
retValue = 0;
break;
....
}
In this example the memory is copied from 'data' to 'ifreqp->ifr_data', at the same time the size of the
memory to be copied is sizeof(data), i.e. 4 or 8 bytes depending on the bitness of the architecture.
Pointers
V557 Array overrun is possible. The '2' index is pointing beyond array bound. if_spppsubr.c 4348
#define AUTHKEYLEN 16
struct sauth {
u_short proto; /* authentication protocol to use */
u_short flags;
#define AUTHFLAG_NOCALLOUT 1
/* callouts */
#define AUTHFLAG_NORECHALLENGE 2 /* do not re-challenge CHAP */
u_char name[AUTHNAMELEN]; /* system identification name */
u_char secret[AUTHKEYLEN]; /* secret password */
u_char challenge[AUTHKEYLEN]; /* random challenge */
};
static void
sppp_chap_scr(struct sppp *sp)
{
u_long *ch, seed;
u_char clen;
/* Compute random challenge. */
ch = (u_long *)sp->myauth.challenge;
read_random(&seed, sizeof seed);
ch[0] = seed ^ random();
ch[1] = seed ^ random();
ch[2] = seed ^ random(); // <=
ch[3] = seed ^ random(); // <=
clen = AUTHKEYLEN;
....
}
The size of 'u_char' type is 1 byte in the 32 and 64 - bit applications; but the size of the 'u_long' type is 4
bytes in the 32-bit applications and 8 byte in the 64-bit application. So in the 32-bit application during
the execution of the operation "u_long* ch = (u_long *)sp->myauth.challenge", the array 'ch' will consist
of 4 elements, 4 bytes each. And in the 64-bit application the array 'ch' will consist of 2 elements, that
have 8 bytes each. Therefore, if we compile the 64-bit kernel, then when accessing ch[2] and ch[3] we'll
have array index out of bounds.
V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_plex.c 173
gv_plex_offset(...., int *sdno, int growing)
{
....
*sdno = stripeno % sdcount;
....
KASSERT(sdno >= 0, ("gv_plex_offset: sdno < 0"));
....
}
We managed to detect a very interesting fragment with the help of diagnostic 503. There is no point in
checking that the pointer is greater than or equal to 0. Most likely, the pointer "sdno" was not
dereferenced in order to compare the stored value.
There are two more comparisons with null.
 V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_raid5.c 602
 V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_raid5.c 610
V522 Dereferencing of the null pointer 'sc' might take place. mrsas.c 4027
void
mrsas_aen_handler(struct mrsas_softc *sc)
{
....
if (!sc) {
device_printf(sc->mrsas_dev, "invalid instance!n");
return;
}
if (sc->evt_detail_mem) {
....
}
If the pointer "sc" is a null one, then the function will exit. However, it's not quite clear, why the
programmer tried to dereference the "sc->mrsas_dev" pointer.
A list of strange fragments:
 V522 Dereferencing of the null pointer 'sc' might take place. mrsas.c 1279
 V522 Dereferencing of the null pointer 'sc' might take place. tws_cam.c 1066
 V522 Dereferencing of the null pointer 'sc' might take place. blkfront.c 677
 V522 Dereferencing of the null pointer 'dev_priv' might take place. radeon_cs.c 153
 V522 Dereferencing of the null pointer 'ha' might take place. ql_isr.c 728
V713 The pointer m was utilized in the logical expression before it was verified against nullptr in the
same logical expression. ip_fastfwd.c 245
struct mbuf *
ip_tryforward(struct mbuf *m)
{
....
if (pfil_run_hooks(
&V_inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL) ||
m == NULL)
goto drop;
....
}
The check "m == NULL" is placed incorrectly. First we need to check the pointer, and only then call the
pfil_run_hooks() function.
Loops
V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or
won't be executed at all. if_ae.c 1663
#define AE_IDLE_TIMEOUT 100
static void
ae_stop_rxmac(ae_softc_t *sc)
{
int i;
....
/*
* Wait for IDLE state.
*/
for (i = 0; i < AE_IDLE_TIMEOUT; i--) { // <=
val = AE_READ_4(sc, AE_IDLE_REG);
if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0)
break;
DELAY(100);
}
....
}
In the source code of FreeBSD we found such an interesting and incorrect loop. For some reason, there
is a decrement of a loop counter instead of an increment. It turns out that the loop can execute more
times than the value of AE_IDLE_TIMEOUT, until the 'break' operator executes.
If the loop is not stopped, then we'll have the overflow of a signed variable 'i'. Signed variable overflow
is nothing but a undefined behavior. And it's not some abstract theoretical danger, it is very real.
Recently, my colleague wrote an article on this topic: Undefined behavior is closer than you think
One more interesting moment. We detected the same error in the code of Haiku operating system (see
the section "Warnings #17, #18") No idea, who borrowed the "if_ae.c" file, but this error appears after
Copy-Paste.
V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 182, 183. mfi_tbolt.c
183
mfi_tbolt_adp_reset(struct mfi_softc *sc)
{
....
for (i=0; i < 10; i++) {
for (i = 0; i < 10000; i++);
}
....
}
Probably, this small piece of code is used for creating the delay, but in sum total only 10000 operations
are executed, not 10*10000; why then 2 loops are needed here?
I specifically cited this example because it is the most vivid to show that the usage of the same variable
in the external and nested loops leads to unexpected results.
V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 197, 208.
linux_vdso.c 208
void
__elfN(linux_vdso_reloc)(struct sysentvec *sv, long vdso_adjust)
{
....
for(i = 0; i < ehdr->e_shnum; i++) { // <=
if (!(shdr[i].sh_flags & SHF_ALLOC))
continue;
shdr[i].sh_addr += vdso_adjust;
if (shdr[i].sh_type != SHT_SYMTAB &&
shdr[i].sh_type != SHT_DYNSYM)
continue;
sym = (Elf_Sym *)((caddr_t)ehdr + shdr[i].sh_offset);
symcnt = shdr[i].sh_size / sizeof(*sym);
for(i = 0; i < symcnt; i++, sym++) { // <=
if (sym->st_shndx == SHN_UNDEF ||
sym->st_shndx == SHN_ABS)
continue;
sym->st_value += vdso_adjust;
}
}
....
}
This is probably a too complicated example to understand if the code executes correctly. But looking at
the previous example we can draw a conclusion that a wrong number of iterations is executed here as
well.
V547 Expression 'j >= 0' is always true. Unsigned type value is always >= 0. safe.c 1596
static void
safe_mcopy(struct mbuf *srcm, struct mbuf *dstm, u_int offset)
{
u_int j, dlen, slen; // <=
caddr_t dptr, sptr;
/*
* Advance src and dst to offset.
*/
j = offset;
while (j >= 0) { // <=
if (srcm->m_len > j)
break;
j -= srcm->m_len; // <=
srcm = srcm->m_next;
if (srcm == NULL)
return;
}
sptr = mtod(srcm, caddr_t) + j;
slen = srcm->m_len - j;
j = offset;
while (j >= 0) { // <=
if (dstm->m_len > j)
break;
j -= dstm->m_len; // <=
dstm = dstm->m_next;
if (dstm == NULL)
return;
}
dptr = mtod(dstm, caddr_t) + j;
dlen = dstm->m_len - j;
....
}
There are two dangerous loops in this function. As the 'j' variable (loop counters) has an unsigned type,
then the "j >= 0" check is always true and these loops are "infinity". Another problem is that some value
is constantly subtracted from this counter; therefore if there is an attempt to access beyond the zero
value, then the 'j' variable will get the maximum value of its type.
V711 It is dangerous to create a local variable within a loop with a same name as a variable controlling
this loop. powernow.c 73
static int
pn_decode_pst(device_t dev)
{
....
struct pst_header *pst; // <=
....
p = ((uint8_t *) psb) + sizeof(struct psb_header);
pst = (struct pst_header*) p;
maxpst = 200;
do {
struct pst_header *pst = (struct pst_header*) p; // <=
....
p += sizeof(struct pst_header) + (2 * pst->numpstates);
} while (cpuid_is_k7(pst->cpuid) && maxpst--); // <=
....
}
In the body of the loop we detected variable declaration was that matches the variable used for the loop
control. I suspect that the value of the external pointer with the 'pst' name doesn't change because a
local pointer with the same 'pst' is created. Perhaps the same "pst->cupid" value is always checked in
the loop condition do....while(). The developers should review this fragment and give the variables
different names.
Miscelleneous
V569 Truncation of constant value -96. The value range of unsigned char type: [0, 255]. if_rsu.c 1516
struct ieee80211_rx_stats {
....
uint8_t nf; /* global NF */
uint8_t rssi; /* global RSSI */
....
};
static void
rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
{
....
rxs.rssi = le32toh(bss->rssi) / 2;
rxs.nf = -96;
....
}
It's very strange that an unsigned variable "rxs.nf" is assigned with a negative value '-96' As a result, the
variable will have the value '160'.
V729 Function body contains the 'done' label that is not used by any 'goto' statements. zfs_acl.c 2023
int
zfs_setacl(znode_t *zp, vsecattr_t *vsecp, ....)
{
....
top:
mutex_enter(&zp->z_acl_lock);
mutex_enter(&zp->z_lock);
....
if (error == ERESTART) {
dmu_tx_wait(tx);
dmu_tx_abort(tx);
goto top;
}
....
done: // <=
mutex_exit(&zp->z_lock);
mutex_exit(&zp->z_acl_lock);
return (error);
}
In this code there are functions containing labels, but at the same time, the call of the 'goto' statement
is missing for these labels. For example, we see that the 'top' label is used in this fragment, but 'done'
isn't used anywhere. Perhaps the programmer forgot to add a jump to the label, or it was removed over
the time, while the label was left in the code.
V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing.
mac_process.c 352
static void
mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred,
struct vm_map *map)
{
....
if (!mac_mmap_revocation_via_cow) {
vme->max_protection &= ~VM_PROT_WRITE;
vme->protection &= ~VM_PROT_WRITE;
} if ((revokeperms & VM_PROT_READ) == 0) // <=
vme->eflags |= MAP_ENTRY_COW |
MAP_ENTRY_NEEDS_COPY;
....
}
Finally, I want to tell you about suspicious formatting, which I already came across in the very beginning
of the project check. Here the code is aligned in such a way that the absence of the keyword 'else' looks
strange.
V705 It is possible that 'else' block was forgotten or commented out, thus altering the program's
operation logics. scsi_da.c 3231
static void
dadone(struct cam_periph *periph, union ccb *done_ccb)
{
....
/*
* If we tried READ CAPACITY(16) and failed,
* fallback to READ CAPACITY(10).
*/
if ((state == DA_CCB_PROBE_RC16) &&
....
} else // <=
/*
* Attach to anything that claims to be a
* direct access or optical disk device,
* as long as it doesn't return a "Logical
* unit not supported" (0x25) error.
*/
if ((have_sense) && (asc != 0x25) // <=
....
} else {
....
}
....
}
This code has no error now, but it will definitely show up one day. By leaving such a big commentary
before 'else' you may accidentally forget that this keyword was somewhere in the code and make some
erroneous edits.
Conclusion
The FreeBSD project was tested by special version of PVS-Studio, which showed a great result! The
whole material is impossible to fit in one article. Nevertheless, the development team of FreeBSD got
the full list of the analyzer warnings that should be examined.
I suggest everyone to try PVS-Studio on your projects. The analyzer works in Windows environment. We
don't have a public version for using the analyzer in the development of the projects for Linux/FreeBSD.
We could also discuss possible variants of customization PVS-Studio for your projects and specific tasks.

Weitere ähnliche Inhalte

Was ist angesagt?

Heading for a Record: Chromium, the 5th Check
Heading for a Record: Chromium, the 5th CheckHeading for a Record: Chromium, the 5th Check
Heading for a Record: Chromium, the 5th CheckPVS-Studio
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckAndrey Karpov
 
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source CodeA Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source CodePVS-Studio
 
Python and Ruby implementations compared by the error density
Python and Ruby implementations compared by the error densityPython and Ruby implementations compared by the error density
Python and Ruby implementations compared by the error densityPVS-Studio
 
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Andrey Karpov
 
Re-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large reportRe-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large reportPVS-Studio
 
Rechecking Apache HTTP Server
Rechecking Apache HTTP ServerRechecking Apache HTTP Server
Rechecking Apache HTTP ServerPVS-Studio
 
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible IssuesPVS-Studio
 
Checking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzerChecking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzerPVS-Studio
 
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ..."Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...PVS-Studio
 
PVS-Studio Meets Octave
PVS-Studio Meets Octave PVS-Studio Meets Octave
PVS-Studio Meets Octave PVS-Studio
 
Asterisk: PVS-Studio Takes Up Telephony
Asterisk: PVS-Studio Takes Up TelephonyAsterisk: PVS-Studio Takes Up Telephony
Asterisk: PVS-Studio Takes Up TelephonyAndrey Karpov
 
Integration Project Inspection 3
Integration Project Inspection 3Integration Project Inspection 3
Integration Project Inspection 3Dillon Lee
 
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
Waiting for the Linux-version: Checking the Code of Inkscape Graphics EditorWaiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
Waiting for the Linux-version: Checking the Code of Inkscape Graphics EditorPVS-Studio
 
Checking Oracle VM VirtualBox. Part 1
Checking Oracle VM VirtualBox. Part 1Checking Oracle VM VirtualBox. Part 1
Checking Oracle VM VirtualBox. Part 1Andrey Karpov
 
Checking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto GameChecking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto GameAndrey Karpov
 
Valgrind debugger Tutorial
Valgrind debugger TutorialValgrind debugger Tutorial
Valgrind debugger TutorialAnurag Tomar
 
PVS-Studio team is about to produce a technical breakthrough, but for now let...
PVS-Studio team is about to produce a technical breakthrough, but for now let...PVS-Studio team is about to produce a technical breakthrough, but for now let...
PVS-Studio team is about to produce a technical breakthrough, but for now let...PVS-Studio
 

Was ist angesagt? (20)

Heading for a Record: Chromium, the 5th Check
Heading for a Record: Chromium, the 5th CheckHeading for a Record: Chromium, the 5th Check
Heading for a Record: Chromium, the 5th Check
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after Cppcheck
 
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source CodeA Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
 
Python and Ruby implementations compared by the error density
Python and Ruby implementations compared by the error densityPython and Ruby implementations compared by the error density
Python and Ruby implementations compared by the error density
 
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
 
Re-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large reportRe-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large report
 
Rechecking Apache HTTP Server
Rechecking Apache HTTP ServerRechecking Apache HTTP Server
Rechecking Apache HTTP Server
 
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
 
Checking VirtualDub
Checking VirtualDubChecking VirtualDub
Checking VirtualDub
 
Checking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzerChecking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzer
 
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ..."Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
 
PVS-Studio Meets Octave
PVS-Studio Meets Octave PVS-Studio Meets Octave
PVS-Studio Meets Octave
 
Asterisk: PVS-Studio Takes Up Telephony
Asterisk: PVS-Studio Takes Up TelephonyAsterisk: PVS-Studio Takes Up Telephony
Asterisk: PVS-Studio Takes Up Telephony
 
Integration Project Inspection 3
Integration Project Inspection 3Integration Project Inspection 3
Integration Project Inspection 3
 
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
Waiting for the Linux-version: Checking the Code of Inkscape Graphics EditorWaiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
 
Checking Oracle VM VirtualBox. Part 1
Checking Oracle VM VirtualBox. Part 1Checking Oracle VM VirtualBox. Part 1
Checking Oracle VM VirtualBox. Part 1
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0
 
Checking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto GameChecking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto Game
 
Valgrind debugger Tutorial
Valgrind debugger TutorialValgrind debugger Tutorial
Valgrind debugger Tutorial
 
PVS-Studio team is about to produce a technical breakthrough, but for now let...
PVS-Studio team is about to produce a technical breakthrough, but for now let...PVS-Studio team is about to produce a technical breakthrough, but for now let...
PVS-Studio team is about to produce a technical breakthrough, but for now let...
 

Andere mochten auch

Tlotlego MTA Certificate
Tlotlego MTA CertificateTlotlego MTA Certificate
Tlotlego MTA CertificateTlotlego Mankwe
 
Netflix Update (MeetBSD California 2014 Lightning Talk)
Netflix Update (MeetBSD California 2014 Lightning Talk)Netflix Update (MeetBSD California 2014 Lightning Talk)
Netflix Update (MeetBSD California 2014 Lightning Talk)iXsystems
 
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsApp
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsAppScaling to Millions of Simultaneous Connections by Rick Reed from WhatsApp
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsAppmustafa sarac
 
FreeBSD vs Linux, RMLL 2014
FreeBSD vs Linux, RMLL 2014FreeBSD vs Linux, RMLL 2014
FreeBSD vs Linux, RMLL 2014Loïc Tosser
 
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)iXsystems
 
What's new in FreeBSD 10
What's new in FreeBSD 10What's new in FreeBSD 10
What's new in FreeBSD 10Gleb Smirnoff
 
Python on FreeBSD
Python on FreeBSDPython on FreeBSD
Python on FreeBSDpycontw
 
Sony C#/.NET component set analysis
Sony C#/.NET component set analysisSony C#/.NET component set analysis
Sony C#/.NET component set analysisPVS-Studio
 
An Ideal Way to Integrate a Static Code Analyzer into a Project
An Ideal Way to Integrate a Static Code Analyzer into a ProjectAn Ideal Way to Integrate a Static Code Analyzer into a Project
An Ideal Way to Integrate a Static Code Analyzer into a ProjectPVS-Studio
 
Analyzing source code of WPF examples by the Infragistics Company
Analyzing source code of WPF examples by the Infragistics CompanyAnalyzing source code of WPF examples by the Infragistics Company
Analyzing source code of WPF examples by the Infragistics CompanyPVS-Studio
 
The tyranny of averages
The tyranny of averagesThe tyranny of averages
The tyranny of averagesPVS-Studio
 
Analyzing Wine: One Year Later
Analyzing Wine: One Year LaterAnalyzing Wine: One Year Later
Analyzing Wine: One Year LaterPVS-Studio
 
Cuba FAM Report for Travel Agents
Cuba FAM Report for Travel AgentsCuba FAM Report for Travel Agents
Cuba FAM Report for Travel AgentsHaley Whitelaw
 
Comentário: 3° Domingo da Quaresma - Ano C
Comentário: 3° Domingo da Quaresma - Ano CComentário: 3° Domingo da Quaresma - Ano C
Comentário: 3° Domingo da Quaresma - Ano CJosé Lima
 
Owyhee Canyonlands
Owyhee CanyonlandsOwyhee Canyonlands
Owyhee CanyonlandsLacy Turner
 

Andere mochten auch (19)

Tlotlego MTA Certificate
Tlotlego MTA CertificateTlotlego MTA Certificate
Tlotlego MTA Certificate
 
Netflix Update (MeetBSD California 2014 Lightning Talk)
Netflix Update (MeetBSD California 2014 Lightning Talk)Netflix Update (MeetBSD California 2014 Lightning Talk)
Netflix Update (MeetBSD California 2014 Lightning Talk)
 
P05-slides
P05-slidesP05-slides
P05-slides
 
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsApp
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsAppScaling to Millions of Simultaneous Connections by Rick Reed from WhatsApp
Scaling to Millions of Simultaneous Connections by Rick Reed from WhatsApp
 
FreeBSD vs Linux, RMLL 2014
FreeBSD vs Linux, RMLL 2014FreeBSD vs Linux, RMLL 2014
FreeBSD vs Linux, RMLL 2014
 
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)
600M+ Unsuspecting FreeBSD Users (MeetBSD California 2014)
 
What's new in FreeBSD 10
What's new in FreeBSD 10What's new in FreeBSD 10
What's new in FreeBSD 10
 
Python on FreeBSD
Python on FreeBSDPython on FreeBSD
Python on FreeBSD
 
Sony C#/.NET component set analysis
Sony C#/.NET component set analysisSony C#/.NET component set analysis
Sony C#/.NET component set analysis
 
An Ideal Way to Integrate a Static Code Analyzer into a Project
An Ideal Way to Integrate a Static Code Analyzer into a ProjectAn Ideal Way to Integrate a Static Code Analyzer into a Project
An Ideal Way to Integrate a Static Code Analyzer into a Project
 
Analyzing source code of WPF examples by the Infragistics Company
Analyzing source code of WPF examples by the Infragistics CompanyAnalyzing source code of WPF examples by the Infragistics Company
Analyzing source code of WPF examples by the Infragistics Company
 
The tyranny of averages
The tyranny of averagesThe tyranny of averages
The tyranny of averages
 
Analyzing Wine: One Year Later
Analyzing Wine: One Year LaterAnalyzing Wine: One Year Later
Analyzing Wine: One Year Later
 
TARUN KUMAR SINGH
TARUN KUMAR SINGHTARUN KUMAR SINGH
TARUN KUMAR SINGH
 
Cuba FAM Report for Travel Agents
Cuba FAM Report for Travel AgentsCuba FAM Report for Travel Agents
Cuba FAM Report for Travel Agents
 
Typing Project
Typing ProjectTyping Project
Typing Project
 
Comentário: 3° Domingo da Quaresma - Ano C
Comentário: 3° Domingo da Quaresma - Ano CComentário: 3° Domingo da Quaresma - Ano C
Comentário: 3° Domingo da Quaresma - Ano C
 
Owyhee Canyonlands
Owyhee CanyonlandsOwyhee Canyonlands
Owyhee Canyonlands
 
vishal kumar
vishal kumarvishal kumar
vishal kumar
 

Ähnlich wie PVS-Studio delved into the FreeBSD kernel

Linux Kernel, tested by the Linux-version of PVS-Studio
Linux Kernel, tested by the Linux-version of PVS-StudioLinux Kernel, tested by the Linux-version of PVS-Studio
Linux Kernel, tested by the Linux-version of PVS-StudioPVS-Studio
 
Checking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-StudioChecking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-StudioAndrey Karpov
 
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' RequestChecking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' RequestPVS-Studio
 
The Little Unicorn That Could
The Little Unicorn That CouldThe Little Unicorn That Could
The Little Unicorn That CouldPVS-Studio
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionAndrey Karpov
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionPVS-Studio
 
CppCat Static Analyzer Review
CppCat Static Analyzer ReviewCppCat Static Analyzer Review
CppCat Static Analyzer ReviewAndrey Karpov
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0PVS-Studio
 
The CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitThe CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitAndrey Karpov
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects 100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects Andrey Karpov
 
I want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel companyI want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel companyPVS-Studio
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionPVS-Studio
 
Analysis of Godot Engine's Source Code
Analysis of Godot Engine's Source CodeAnalysis of Godot Engine's Source Code
Analysis of Godot Engine's Source CodePVS-Studio
 
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017Andrey Karpov
 
Firefox Easily Analyzed by PVS-Studio Standalone
Firefox Easily Analyzed by PVS-Studio StandaloneFirefox Easily Analyzed by PVS-Studio Standalone
Firefox Easily Analyzed by PVS-Studio StandaloneAndrey Karpov
 
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderA Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderAndrey Karpov
 
PVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd CheckPVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd CheckAndrey Karpov
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project AnalyzedPVS-Studio
 
PVS-Studio for Linux Went on a Tour Around Disney
PVS-Studio for Linux Went on a Tour Around DisneyPVS-Studio for Linux Went on a Tour Around Disney
PVS-Studio for Linux Went on a Tour Around DisneyPVS-Studio
 
Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016PVS-Studio
 

Ähnlich wie PVS-Studio delved into the FreeBSD kernel (20)

Linux Kernel, tested by the Linux-version of PVS-Studio
Linux Kernel, tested by the Linux-version of PVS-StudioLinux Kernel, tested by the Linux-version of PVS-Studio
Linux Kernel, tested by the Linux-version of PVS-Studio
 
Checking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-StudioChecking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-Studio
 
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' RequestChecking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
 
The Little Unicorn That Could
The Little Unicorn That CouldThe Little Unicorn That Could
The Little Unicorn That Could
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
 
CppCat Static Analyzer Review
CppCat Static Analyzer ReviewCppCat Static Analyzer Review
CppCat Static Analyzer Review
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0
 
The CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitThe CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGit
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects 100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects
 
I want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel companyI want to sell a PVS-Studio license to the Intel company
I want to sell a PVS-Studio license to the Intel company
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
 
Analysis of Godot Engine's Source Code
Analysis of Godot Engine's Source CodeAnalysis of Godot Engine's Source Code
Analysis of Godot Engine's Source Code
 
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
 
Firefox Easily Analyzed by PVS-Studio Standalone
Firefox Easily Analyzed by PVS-Studio StandaloneFirefox Easily Analyzed by PVS-Studio Standalone
Firefox Easily Analyzed by PVS-Studio Standalone
 
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderA Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
 
PVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd CheckPVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd Check
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project Analyzed
 
PVS-Studio for Linux Went on a Tour Around Disney
PVS-Studio for Linux Went on a Tour Around DisneyPVS-Studio for Linux Went on a Tour Around Disney
PVS-Studio for Linux Went on a Tour Around Disney
 
Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016
 

Kürzlich hochgeladen

Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 

Kürzlich hochgeladen (20)

Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 

PVS-Studio delved into the FreeBSD kernel

  • 1. PVS-Studio delved into the FreeBSD kernel Author: Svyatoslav Razmyslov Date: 17.02.2016 About a year ago we checked the Linux core. It was one of the most discussed articles at that time. We also got quite a number of requests to check FreeBSD, so finally we decided to take the time to do it. About the project FreeBSD is a contemporary operating system for servers, desktops and embedded computer platforms. Its code has gone through more than thirty years of continuous development, improvement and optimization. It has proven itself as a system for building intranet, Internet networks, and servers. It provides reliable network services and efficient memory management. Despite the fact that FreeBSD is regularly checked by Coverity, we had a great time checking this project because a lot of suspicious fragments were found. In this article we'll provide about 40 fragments, but the developers of this project may have a look at a full list, which contains around 1000 analyzer warnings of high severity. To my humble opinion, a lot of those warnings issued by the analyzer are real bugs, but it's hard for me to determine how critical they are, as I am not the developer of the system. I suppose it could be a good ground for a discussion with the authors of the project. The source code was taken from GitHub branch - 'master'. The repository contains ~23000 files and two dozens of assembly configurations for different platforms, but I checked the kernel only, which I compiled in this way: # make buildkernel KERNCONF=MYKERNEL Methodology We used static code analyzer PVS-Studio, version 6.01.
  • 2. For convenience, I set a PC-BSD and wrote a small utility in C++, which keeps the working environment of the compilers' runs when building the kernel. The acquired information was used to get the preprocessed files and their analysis, done by PVS-Studio. This method allowed me to quickly check a project without having to study an unfamiliar build system to integrate the analyzer. On top of it, analysis of preprocessed files allows you to do a more in-depth analysis of the code and find more sophisticated and interesting, errors, in macros for instance. This article will provide several examples of such a kind. Linux kernel was analyzed in the same way; this mode is also available for Windows users in the Standalone utility, which is a part of PVS-studio distribution kit. Usually PVS-Studio seamlessly integrates into the projects. There is a number of ways to integrate the analyzer, described in the documentation. Monitoring utilities have a big advantage of trying the analyzer if the project has an unusual building system. Surprising luck The first possible error was found before I ran the analyzer on the project, and even before I built the kernel; the build was interrupted by a linking error. Having addressed the file, specified in the error, I saw the following:
  • 3. Pay attention to the highlighted fragment: a tab character is used for the formatting of the indentations; two statements are moved under the condition. But the last statement does not actually refer to a condition and will be always executed. Perhaps, curly braces were forgotten here. Once we got a comment that we just copy the analyzer warnings, but it is not so. Before the analysis of the project we have to make sure that it gets compiled correctly; when the report is done, the warnings must be sorted/examined and commented. The same work is done by our customer support team, when they answer the incoming mails. There are also cases when the customers send examples of false positives (in their opinion) which turn out to be real bugs. Capy-poste and typos PVS-Studio analyzer is a powerful tool for static code analysis that finds bugs of various levels of severity. The first diagnostics were very simple and were created to detect most common bugs, related to typos and copy-paste programming. After the analysis review, I sort them according to the error code. So in this article we'll start with this type of diagnostic rules. V501 There are identical sub-expressions '(uintptr_t) b->handler' to the left and to the right of the '>' operator. ip_fw_sockopt.c 2893 static int compare_sh(const void *_a, const void *_b) { const struct ipfw_sopt_handler *a, *b; a = (const struct ipfw_sopt_handler *)_a; b = (const struct ipfw_sopt_handler *)_b; .... if ((uintptr_t)a->handler < (uintptr_t)b->handler) return (-1); else if ((uintptr_t)b->handler > (uintptr_t)b->handler) // <= return (1); return (0); } Here is a vivid example of a bad practice - giving the variables short and uninformative names. Now because of the typo in the letter 'b', the a part of the condition will never be return 1. Thus, the function returns a zero status not always correctly. V501 There are identical sub-expressions to the left and to the right of the '!=' operator: m- >m_pkthdr.len != m->m_pkthdr.len key.c 7208 int key_parse(struct mbuf *m, struct socket *so) { .... if ((m->m_flags & M_PKTHDR) == 0 ||
  • 4. m->m_pkthdr.len != m->m_pkthdr.len) { // <= .... goto senderror; } .... } One of the fields of the structure is compared with itself; therefore, the result of the logical operation will always be False. V501 There are identical sub-expressions to the left and to the right of the '|' operator: PIM_NOBUSRESET | PIM_NOBUSRESET sbp_targ.c 1327 typedef enum { PIM_EXTLUNS = 0x100, PIM_SCANHILO = 0x80, PIM_NOREMOVE = 0x40, PIM_NOINITIATOR = 0x20, PIM_NOBUSRESET = 0x10, // <= PIM_NO_6_BYTE = 0x08, PIM_SEQSCAN = 0x04, PIM_UNMAPPED = 0x02, PIM_NOSCAN = 0x01 } pi_miscflag; static void sbp_targ_action1(struct cam_sim *sim, union ccb *ccb) { .... struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_TAG_ABLE; cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; cpi->transport = XPORT_SPI; cpi->hba_misc = PIM_NOBUSRESET | PIM_NOBUSRESET; // <= .... } In this example we see that the same variable "PIM_NOBUSRESET" is used in the bitwise operation, which doesn't affect the result in any way. Most likely a constant with a different value was meant to be used here, but the variable was left unchanged. V523 The 'then' statement is equivalent to the 'else' statement. saint.c 2023 GLOBAL void siSMPRespRcvd(....) { .... if (agNULL == frameHandle) { /* indirect mode */ /* call back with success */ (*(ossaSMPCompletedCB_t)(pRequest->completionCB))(agRoot, pRequest->pIORequestContext, OSSA_IO_SUCCESS, payloadSize,
  • 5. frameHandle); } else { /* direct mode */ /* call back with success */ (*(ossaSMPCompletedCB_t)(pRequest->completionCB))(agRoot, pRequest->pIORequestContext, OSSA_IO_SUCCESS, payloadSize, frameHandle); } .... } Two condition branches are commented differently: /* indirect mode */ and /* direct mode */, but they are implemented similarly, which is very suspicious. V523 The 'then' statement is equivalent to the 'else' statement. smsat.c 2848 osGLOBAL void smsatInquiryPage89(....) { .... if (oneDeviceData->satDeviceType == SATA_ATA_DEVICE) { pInquiry[40] = 0x01; /* LBA Low */ pInquiry[41] = 0x00; /* LBA Mid */ pInquiry[42] = 0x00; /* LBA High */ pInquiry[43] = 0x00; /* Device */ pInquiry[44] = 0x00; /* LBA Low Exp */ pInquiry[45] = 0x00; /* LBA Mid Exp */ pInquiry[46] = 0x00; /* LBA High Exp */ pInquiry[47] = 0x00; /* Reserved */ pInquiry[48] = 0x01; /* Sector Count */ pInquiry[49] = 0x00; /* Sector Count Exp */ } else { pInquiry[40] = 0x01; /* LBA Low */ pInquiry[41] = 0x00; /* LBA Mid */ pInquiry[42] = 0x00; /* LBA High */ pInquiry[43] = 0x00; /* Device */ pInquiry[44] = 0x00; /* LBA Low Exp */ pInquiry[45] = 0x00; /* LBA Mid Exp */ pInquiry[46] = 0x00; /* LBA High Exp */ pInquiry[47] = 0x00; /* Reserved */ pInquiry[48] = 0x01; /* Sector Count */ pInquiry[49] = 0x00; /* Sector Count Exp */ } .... } This example is even more suspicious than the previous one. A big code fragment was copied, but later no changes were made. V547 Expression is always true. Probably the '&&' operator should be used here. qla_hw.c 799
  • 6. static int qla_tx_tso(qla_host_t *ha, struct mbuf *mp, ....) { .... if ((*tcp_opt != 0x01) || (*(tcp_opt + 1) != 0x01) || (*(tcp_opt + 2) != 0x08) || (*(tcp_opt + 2) != 10)) { // <= return -1; } .... } Here the analyzer detected that the condition "(* (tcp_opt + 2)! = 0x08) | (* (tcp_opt + 2)! = 10) "is always true and it is really so, if you build a truth table. But most likely the '&&' is not needed here, it is just a typo in the address offset. Perhaps the function code should be like this: static int qla_tx_tso(qla_host_t *ha, struct mbuf *mp, ....) { .... if ((*tcp_opt != 0x01) || (*(tcp_opt + 1) != 0x01) || (*(tcp_opt + 2) != 0x08) || (*(tcp_opt + 3) != 10)) { return -1; } .... } V571 Recurring check. This condition was already verified in line 1946. sahw.c 1949 GLOBAL bit32 siHDAMode_V(....) { .... if( saRoot->memoryAllocated.agMemory[i].totalLength > biggest) { if(biggest < saRoot->memoryAllocated.agMemory[i].totalLength) { save = i; biggest = saRoot->memoryAllocated.agMemory[i].totalLength; } } .... } This code is really strange, if we simplify it, we'll see the following: if( A > B ) { if (B < A) { .... } } The same condition is checked twice. Most likely, something else was supposed to be written here. A similar fragment:
  • 7.  V571 Recurring check. This condition was already verified in line 1940. if_rl.c 1941 Dangerous macros V523 The 'then' statement is equivalent to the 'else' statement. agtiapi.c 829 if (osti_strncmp(buffer, "0x", 2) == 0) { maxTargets = osti_strtoul (buffer, &pLastUsedChar, 0); AGTIAPI_PRINTK( ".... maxTargets = osti_strtoul 0 n" ); } else { maxTargets = osti_strtoul (buffer, &pLastUsedChar, 10); AGTIAPI_PRINTK( ".... maxTargets = osti_strtoul 10n" ); } First, I skipped this analyzer warning, thinking that it is a false positive. But warnings of low severity should also be reviewed after the project check (to improve the analyzer). So I came across such a macro: #define osti_strtoul(nptr, endptr, base) strtoul((char *)nptr, (char **)endptr, 0) The 'base' parameter isn't used at all, and the '0' value is always passed to the "strtoul" function as the last parameter, although values '0' and '10' are passed to the macro. In the preprocessed files all macros got expanded and the code became similar. This macro is used in this way several dozen times. The entire list of such fragments was sent to the developers. V733 It is possible that macro expansion resulted in incorrect evaluation order. Check expression: chan - 1 * 20. isp.c 2301 static void isp_fibre_init_2400(ispsoftc_t *isp) .... if (ISP_CAP_VP0(isp)) off += ICB2400_VPINFO_PORT_OFF(chan); else off += ICB2400_VPINFO_PORT_OFF(chan - 1); // <= .... } At first glance, there is nothing strange in this code fragment. We see that sometimes the 'chan' value is used, sometimes less by one 'chan - 1', but let's have look at the macro definition: #define ICB2400_VPOPT_WRITE_SIZE 20 #define ICB2400_VPINFO_PORT_OFF(chan) (ICB2400_VPINFO_OFF + sizeof (isp_icb_2400_vpinfo_t) + (chan * ICB2400_VPOPT_WRITE_SIZE)) // <= When passing the binary expression to the macro, the calculation logic changes dramatically. The expression "(chan - 1) * 20" turns into "chan - 1 *20", i.e. into "chan - 20", and the incorrectly calculated size gets used further in the program.
  • 8. About the priorities of operations In this section, I will discuss how important it is to know the priorities of operations, use extra parentheses, if you are not sure and sometimes test yourself by building truth tables of logical expressions. V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '|' operator. ata-serverworks.c 166 ata_serverworks_chipinit(device_t dev) { .... pci_write_config(dev, 0x5a, (pci_read_config(dev, 0x5a, 1) & ~0x40) | (ctlr->chip->cfg1 == SWKS_100) ? 0x03 : 0x02, 1); } .... } The priority of '?:' operator is lower than of the bitwise OR '|'. As a result, in the bit operations, in addition to the numeric constants, the expression result "(ctlr-> chip > cfg1 = SWKS_100)" gets used, which suddenly changes the calculation/computation logic. Perhaps this error wasn't noticed so far because the result seemed so close to the truth. V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '|' operator. in6.c 1318 void in6_purgeaddr(struct ifaddr *ifa) { .... error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags | (ia->ia_dstaddr.sin6_family == AF_INET6) ? RTF_HOST : 0); .... } A different file also had a fragment with a similar error with a ternary operator. V547 Expression 'cdb[0] != 0x28 || cdb[0] != 0x2A' is always true. Probably the '&&' operator should be used here. mfi_tbolt.c 1110 int mfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm) { ....
  • 9. if (cdb[0] != 0x28 || cdb[0] != 0x2A) { // <=' if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) { device_printf(sc->mfi_dev, "Mapping from MFI " "to MPT Failed n"); return 1; } } else device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIOn"); .... } The first conditional expression is always true, that's why the 'else' branch never gets control. I will provide the truth table in case of controversial logical expressions in this and the following examples. An example for this case: V590 Consider inspecting the 'error == 0 || error != - 1' expression. The expression is excessive or contains a misprint. nd6.c 2119 int nd6_output_ifp(....) { .... /* Use the SEND socket */ error = send_sendso_input_hook(m, ifp, SND_OUT, ip6len); /* -1 == no app on SEND socket */ if (error == 0 || error != -1) // <= return (error); .... } The problem with this fragment is that the conditional expression doesn't depend on the result "error == 0". Perhaps, something is wrong here. Three more cases:  V590 Consider inspecting the 'error == 0 || error != 35' expression. The expression is excessive or contains a misprint. if_ipw.c 1855  V590 Consider inspecting the 'error == 0 || error != 27' expression. The expression is excessive or contains a misprint. if_vmx.c 2747  V547 Expression is always true. Probably the '&&' operator should be used here. igmp.c 1939 V590 Consider inspecting this expression. The expression is excessive or contains a misprint. sig_verify.c 94
  • 10. enum uni_ieact { UNI_IEACT_CLEAR = 0x00, /* clear call */ .... } void uni_mandate_epref(struct uni *uni, struct uni_ie_epref *epref) { .... maxact = -1; FOREACH_ERR(e, uni) { if (e->ie == UNI_IE_EPREF) continue; if (e->act == UNI_IEACT_CLEAR) maxact = UNI_IEACT_CLEAR; else if (e->act == UNI_IEACT_MSG_REPORT) { if (maxact == -1 && maxact != UNI_IEACT_CLEAR) // <= maxact = UNI_IEACT_MSG_REPORT; } else if (e->act == UNI_IEACT_MSG_IGNORE) { if (maxact == -1) maxact = UNI_IEACT_MSG_IGNORE; } } .... } The result of the whole conditional expression doesn'r depend on the calculation of the value "maxact != UNI_IEACT_CLEAR". Here's how it looks in the table: In this section I give three ways of how to make an error in seemingly simple formulas. Just think of it... V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. aacraid.c 2854 #define EINVAL 22 /* Invalid argument */ #define EFAULT 14 /* Bad address */ #define EPERM 1 /* Operation not permitted */ static int aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg) { .... int error, transfer_data = 0; .... if ((error = copyin((void *)&user_srb->data_len, &fibsize, sizeof (u_int32_t)) != 0)) goto out; if (fibsize > (sc->aac_max_fib_size-sizeof(....))) { error = EINVAL; goto out;
  • 11. } if ((error = copyin((void *)user_srb, srbcmd, fibsize) != 0)) goto out; .... out: .... return(error); } In this function the error code gets corrupted, when the assignment is executed in the 'if' operator. I.e. in the expression "error = copyin(...) != 0" the "copyin(...) != 0" is evaluated first, and then the result (0 or 1) is written to the variable 'error'. The documentation for the function 'copyin' states that in case of an error, it returns EFAULT (value 14), and after such a check, the result of a logical operation '1' gets stored in the error code. It is actually EPERM, a completely different error status. Unfortunately, there is quite a number of such fragments.  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. aacraid.c 2861  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_age.c 591  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_alc.c 1535  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_ale.c 606  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_jme.c 807  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_msk.c 1626  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_stge.c 511  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. hunt_filter.c 973  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_smsc.c 1365  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. if_vte.c 431  V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. zfs_vfsops.c 498
  • 12. Strings V541 It is dangerous to print the string 'buffer' into itself. ata-highpoint.c 102 static int ata_highpoint_probe(device_t dev) { .... char buffer[64]; .... strcpy(buffer, "HighPoint "); strcat(buffer, idx->text); if (idx->cfg1 == HPT_374) { if (pci_get_function(dev) == 0) strcat(buffer, " (channel 0+1)"); if (pci_get_function(dev) == 1) strcat(buffer, " (channel 2+3)"); } sprintf(buffer, "%s %s controller", buffer, ata_mode2str(idx->max_dma)); .... } Some string is formed in the buffer. Then the programmer wants to get a new string, saving the previous string value, and add two more words. It seems really simple. To explain why unexpected result will be recieved here, I will quote a simple and clear example from the documentation for this diagnostic: char s[100] = "test"; sprintf(s, "N = %d, S = %s", 123, s); As a result of the work we would want to get the following string: N = 123, S = test But in practice it will be like this: N = 123, S = N = 123, S = In other situations, the same code can lead not only to the incorrect text, but also to the program abortion. The code can be fixed if you use a new buffer to store the result . The correct version: char s1[100] = "test";
  • 13. char s2[100]; sprintf(s2, "N = %d, S = %s", 123, s1); V512 A call of the 'strcpy' function will lead to overflow of the buffer 'p->vendor'. aacraid_cam.c 571 #define SID_VENDOR_SIZE 8 char vendor[SID_VENDOR_SIZE]; #define SID_PRODUCT_SIZE 16 char product[SID_PRODUCT_SIZE]; #define SID_REVISION_SIZE 4 char revision[SID_REVISION_SIZE]; static void aac_container_special_command(struct cam_sim *sim, union ccb *ccb, u_int8_t *cmdp) { .... /* OEM Vendor defines */ strcpy(p->vendor,"Adaptec "); // <= strcpy(p->product,"Array "); // <= strcpy(p->revision,"V1.0"); // <= .... } All three strings here are filled incorrectly. There is no space for null-terminal symbol in the arrays, which may cause serious problems with such strings in the future. One space can be removed in "p- >vendor" and "p->product". Then there will be room for null terminal, that the strcpy() function adds to the end of the string. But there is no free space at all for the end of line characters for the "p->revision"; that's why the value SID_REVISION_SIZE should be increased at least by one. Of course, it is rather hard for me to judge about the code. It's possible, that the terminal null is not needed at all and everything is designed for a specific buffer size. Then the strcpy() function is chosen incorrectly. In this case the code should be written like this: memcpy(p->vendor, "Adaptec ", SID_VENDOR_SIZE); memcpy(p->product, "Array ", SID_PRODUCT_SIZE); memcpy(p->revision, "V1.0", SID_REVISION_SIZE); V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: td->td_name. subr_turnstile.c 1029 static void print_thread(struct thread *td, const char *prefix) { db_printf("%s%p (tid %d, pid %d, ....", prefix, td, td->td_tid, td->td_proc->p_pid, td->td_name[0] != '0' ? td->td_name : td->td_name); } Suspicious fragment. Despite the "td->td_name[0] != '0'" check, this string is still printed. Here are such fragments:  V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: td->td_name. subr_turnstile.c 1112
  • 14.  V583 The '?:' operator, regardless of its conditional expression, always returns one and the same value: td->td_name. subr_turnstile.c 1196 Operations with memory In this section I will tell about incorrect usage of the following functions: void bzero(void *b, size_t len); int copyout(const void *kaddr, void *uaddr, size_t len); V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. osapi.c 316 /* Autosense storage */ struct scsi_sense_data sense_data; void ostiInitiatorIOCompleted(....) { .... bzero(&csio->sense_data, sizeof(&csio->sense_data)); .... } To zero the structure, we should pass the structure pointer and the size of the memory to be zeroed in bytes to the bzero() function; but here the pointer size is passed to the function, not the structure size. The correct code should be like this: bzero(&csio->sense_data, sizeof(csio->sense_data)); V579 The bzero function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. acpi_package.c 83 int acpi_PkgStr(...., void *dst, ....) { .... bzero(dst, sizeof(dst)); .... } In this example we see a similar situation: the size of the pointer, not the object gets passed to the 'bzero' function. Correct version: bzero(dst, sizeof(*dst)); V579 The copyout function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. if_nxge.c 1498 int xge_ioctl_stats(xge_lldev_t *lldev, struct ifreq *ifreqp) {
  • 15. .... *data = (*data == XGE_SET_BUFFER_MODE_1) ? 'Y':'N'; if(copyout(data, ifreqp->ifr_data, sizeof(data)) == 0) // <= retValue = 0; break; .... } In this example the memory is copied from 'data' to 'ifreqp->ifr_data', at the same time the size of the memory to be copied is sizeof(data), i.e. 4 or 8 bytes depending on the bitness of the architecture. Pointers V557 Array overrun is possible. The '2' index is pointing beyond array bound. if_spppsubr.c 4348 #define AUTHKEYLEN 16 struct sauth { u_short proto; /* authentication protocol to use */ u_short flags; #define AUTHFLAG_NOCALLOUT 1 /* callouts */ #define AUTHFLAG_NORECHALLENGE 2 /* do not re-challenge CHAP */ u_char name[AUTHNAMELEN]; /* system identification name */ u_char secret[AUTHKEYLEN]; /* secret password */ u_char challenge[AUTHKEYLEN]; /* random challenge */ }; static void sppp_chap_scr(struct sppp *sp) { u_long *ch, seed; u_char clen; /* Compute random challenge. */ ch = (u_long *)sp->myauth.challenge; read_random(&seed, sizeof seed);
  • 16. ch[0] = seed ^ random(); ch[1] = seed ^ random(); ch[2] = seed ^ random(); // <= ch[3] = seed ^ random(); // <= clen = AUTHKEYLEN; .... } The size of 'u_char' type is 1 byte in the 32 and 64 - bit applications; but the size of the 'u_long' type is 4 bytes in the 32-bit applications and 8 byte in the 64-bit application. So in the 32-bit application during the execution of the operation "u_long* ch = (u_long *)sp->myauth.challenge", the array 'ch' will consist of 4 elements, 4 bytes each. And in the 64-bit application the array 'ch' will consist of 2 elements, that have 8 bytes each. Therefore, if we compile the 64-bit kernel, then when accessing ch[2] and ch[3] we'll have array index out of bounds. V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_plex.c 173 gv_plex_offset(...., int *sdno, int growing) { .... *sdno = stripeno % sdcount; .... KASSERT(sdno >= 0, ("gv_plex_offset: sdno < 0")); .... } We managed to detect a very interesting fragment with the help of diagnostic 503. There is no point in checking that the pointer is greater than or equal to 0. Most likely, the pointer "sdno" was not dereferenced in order to compare the stored value. There are two more comparisons with null.  V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_raid5.c 602  V503 This is a nonsensical comparison: pointer >= 0. geom_vinum_raid5.c 610 V522 Dereferencing of the null pointer 'sc' might take place. mrsas.c 4027 void mrsas_aen_handler(struct mrsas_softc *sc) { .... if (!sc) { device_printf(sc->mrsas_dev, "invalid instance!n"); return; } if (sc->evt_detail_mem) { .... } If the pointer "sc" is a null one, then the function will exit. However, it's not quite clear, why the programmer tried to dereference the "sc->mrsas_dev" pointer. A list of strange fragments:  V522 Dereferencing of the null pointer 'sc' might take place. mrsas.c 1279  V522 Dereferencing of the null pointer 'sc' might take place. tws_cam.c 1066
  • 17.  V522 Dereferencing of the null pointer 'sc' might take place. blkfront.c 677  V522 Dereferencing of the null pointer 'dev_priv' might take place. radeon_cs.c 153  V522 Dereferencing of the null pointer 'ha' might take place. ql_isr.c 728 V713 The pointer m was utilized in the logical expression before it was verified against nullptr in the same logical expression. ip_fastfwd.c 245 struct mbuf * ip_tryforward(struct mbuf *m) { .... if (pfil_run_hooks( &V_inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL) || m == NULL) goto drop; .... } The check "m == NULL" is placed incorrectly. First we need to check the pointer, and only then call the pfil_run_hooks() function. Loops V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all. if_ae.c 1663 #define AE_IDLE_TIMEOUT 100 static void ae_stop_rxmac(ae_softc_t *sc) { int i; .... /* * Wait for IDLE state. */ for (i = 0; i < AE_IDLE_TIMEOUT; i--) { // <= val = AE_READ_4(sc, AE_IDLE_REG); if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0) break; DELAY(100); } ....
  • 18. } In the source code of FreeBSD we found such an interesting and incorrect loop. For some reason, there is a decrement of a loop counter instead of an increment. It turns out that the loop can execute more times than the value of AE_IDLE_TIMEOUT, until the 'break' operator executes. If the loop is not stopped, then we'll have the overflow of a signed variable 'i'. Signed variable overflow is nothing but a undefined behavior. And it's not some abstract theoretical danger, it is very real. Recently, my colleague wrote an article on this topic: Undefined behavior is closer than you think One more interesting moment. We detected the same error in the code of Haiku operating system (see the section "Warnings #17, #18") No idea, who borrowed the "if_ae.c" file, but this error appears after Copy-Paste. V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 182, 183. mfi_tbolt.c 183 mfi_tbolt_adp_reset(struct mfi_softc *sc) { .... for (i=0; i < 10; i++) { for (i = 0; i < 10000; i++); } .... } Probably, this small piece of code is used for creating the delay, but in sum total only 10000 operations are executed, not 10*10000; why then 2 loops are needed here? I specifically cited this example because it is the most vivid to show that the usage of the same variable in the external and nested loops leads to unexpected results. V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 197, 208. linux_vdso.c 208 void __elfN(linux_vdso_reloc)(struct sysentvec *sv, long vdso_adjust) { .... for(i = 0; i < ehdr->e_shnum; i++) { // <= if (!(shdr[i].sh_flags & SHF_ALLOC)) continue; shdr[i].sh_addr += vdso_adjust; if (shdr[i].sh_type != SHT_SYMTAB && shdr[i].sh_type != SHT_DYNSYM) continue; sym = (Elf_Sym *)((caddr_t)ehdr + shdr[i].sh_offset); symcnt = shdr[i].sh_size / sizeof(*sym); for(i = 0; i < symcnt; i++, sym++) { // <= if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_ABS) continue; sym->st_value += vdso_adjust; }
  • 19. } .... } This is probably a too complicated example to understand if the code executes correctly. But looking at the previous example we can draw a conclusion that a wrong number of iterations is executed here as well. V547 Expression 'j >= 0' is always true. Unsigned type value is always >= 0. safe.c 1596 static void safe_mcopy(struct mbuf *srcm, struct mbuf *dstm, u_int offset) { u_int j, dlen, slen; // <= caddr_t dptr, sptr; /* * Advance src and dst to offset. */ j = offset; while (j >= 0) { // <= if (srcm->m_len > j) break; j -= srcm->m_len; // <= srcm = srcm->m_next; if (srcm == NULL) return; } sptr = mtod(srcm, caddr_t) + j; slen = srcm->m_len - j; j = offset; while (j >= 0) { // <= if (dstm->m_len > j) break; j -= dstm->m_len; // <= dstm = dstm->m_next; if (dstm == NULL) return; } dptr = mtod(dstm, caddr_t) + j; dlen = dstm->m_len - j; .... } There are two dangerous loops in this function. As the 'j' variable (loop counters) has an unsigned type, then the "j >= 0" check is always true and these loops are "infinity". Another problem is that some value is constantly subtracted from this counter; therefore if there is an attempt to access beyond the zero value, then the 'j' variable will get the maximum value of its type. V711 It is dangerous to create a local variable within a loop with a same name as a variable controlling this loop. powernow.c 73 static int pn_decode_pst(device_t dev)
  • 20. { .... struct pst_header *pst; // <= .... p = ((uint8_t *) psb) + sizeof(struct psb_header); pst = (struct pst_header*) p; maxpst = 200; do { struct pst_header *pst = (struct pst_header*) p; // <= .... p += sizeof(struct pst_header) + (2 * pst->numpstates); } while (cpuid_is_k7(pst->cpuid) && maxpst--); // <= .... } In the body of the loop we detected variable declaration was that matches the variable used for the loop control. I suspect that the value of the external pointer with the 'pst' name doesn't change because a local pointer with the same 'pst' is created. Perhaps the same "pst->cupid" value is always checked in the loop condition do....while(). The developers should review this fragment and give the variables different names. Miscelleneous V569 Truncation of constant value -96. The value range of unsigned char type: [0, 255]. if_rsu.c 1516 struct ieee80211_rx_stats { .... uint8_t nf; /* global NF */ uint8_t rssi; /* global RSSI */ .... }; static void rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) { .... rxs.rssi = le32toh(bss->rssi) / 2; rxs.nf = -96; .... } It's very strange that an unsigned variable "rxs.nf" is assigned with a negative value '-96' As a result, the variable will have the value '160'. V729 Function body contains the 'done' label that is not used by any 'goto' statements. zfs_acl.c 2023 int zfs_setacl(znode_t *zp, vsecattr_t *vsecp, ....) { .... top: mutex_enter(&zp->z_acl_lock);
  • 21. mutex_enter(&zp->z_lock); .... if (error == ERESTART) { dmu_tx_wait(tx); dmu_tx_abort(tx); goto top; } .... done: // <= mutex_exit(&zp->z_lock); mutex_exit(&zp->z_acl_lock); return (error); } In this code there are functions containing labels, but at the same time, the call of the 'goto' statement is missing for these labels. For example, we see that the 'top' label is used in this fragment, but 'done' isn't used anywhere. Perhaps the programmer forgot to add a jump to the label, or it was removed over the time, while the label was left in the code. V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing. mac_process.c 352 static void mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { .... if (!mac_mmap_revocation_via_cow) { vme->max_protection &= ~VM_PROT_WRITE; vme->protection &= ~VM_PROT_WRITE; } if ((revokeperms & VM_PROT_READ) == 0) // <= vme->eflags |= MAP_ENTRY_COW | MAP_ENTRY_NEEDS_COPY; .... } Finally, I want to tell you about suspicious formatting, which I already came across in the very beginning of the project check. Here the code is aligned in such a way that the absence of the keyword 'else' looks strange. V705 It is possible that 'else' block was forgotten or commented out, thus altering the program's operation logics. scsi_da.c 3231 static void dadone(struct cam_periph *periph, union ccb *done_ccb) { .... /* * If we tried READ CAPACITY(16) and failed, * fallback to READ CAPACITY(10). */ if ((state == DA_CCB_PROBE_RC16) && .... } else // <=
  • 22. /* * Attach to anything that claims to be a * direct access or optical disk device, * as long as it doesn't return a "Logical * unit not supported" (0x25) error. */ if ((have_sense) && (asc != 0x25) // <= .... } else { .... } .... } This code has no error now, but it will definitely show up one day. By leaving such a big commentary before 'else' you may accidentally forget that this keyword was somewhere in the code and make some erroneous edits. Conclusion The FreeBSD project was tested by special version of PVS-Studio, which showed a great result! The whole material is impossible to fit in one article. Nevertheless, the development team of FreeBSD got the full list of the analyzer warnings that should be examined. I suggest everyone to try PVS-Studio on your projects. The analyzer works in Windows environment. We don't have a public version for using the analyzer in the development of the projects for Linux/FreeBSD. We could also discuss possible variants of customization PVS-Studio for your projects and specific tasks.