SlideShare a Scribd company logo
Re-analysis of Umbraco code
Author: Sergey Khrenov
Date: 16.12.2016
Time passes inexorably. It feels that just recently we announced the release of the C# static code
analyzer, checked the first projects, and started writing articles about it. But a whole year has passed
since that moment. It was a year of painstaking and hard work on diagnostic improvements, adding new
diagnostic rules, gathering statistics on false positives and eliminating their causes, communicating with
users, and tackling a great deal of other issues. It was a year of both small and large successes on this
hard, but incredibly interesting, path we have chosen. Now it's time for a re-analysis of the Umbraco
project that we checked right after the release of our C# analyzer a year ago.
Introduction
The first article about the Umbraco analysis was written by my colleague Andrey Karpov. This year the
project continued development, and so far it contains about 3340 files with the extension ".cs", which is
approximately 425 KLOC (at the time of the first check, the project had 3200 files with the extension
".cs", and 400 KLOC respectively).
On the first check, the analyzer found a relatively small number of errors, which were nevertheless quite
interesting to write an article on, and to draw first conclusions about the work of the C# analyzer from.
It is far more interesting doing the check now, when the analyzer has obtained dozens of new diagnostic
rules, and improved it's mechanisms of searching for bugs; it's also quite amusing to compare the results
of the present time check with the one we did a year ago. To do the analysis, I used the latest version of
Umbraco source code, which is also available at GitHub, and also the latest version of PVS-Studio 6.11.
In the results of the check, we got 508 warnings. 71 warnings were first level, 358 - second level, 79 -
third level.
The overall coefficient of issues density (the number of warnings per KLOC) was 1.12. This is a good
indicator which corresponds to approximately one warning per a thousand lines of code. But warnings
do not necessarily mean real errors. It's normal for any static analyzer to have a certain percentage of
false positives. Quite often, the warnings look like real bugs, but later after inspection, it turns out that
it's not so. Therefore, I won't be discussing the low level warnings, as the percentage of false positives is
usually quite high there.
I have reviewed the warnings issued by PVS-Studio, and detected approximately 56% false positives at
High and Meduim levels. The remaining warnings contain quite suspicious constructs that require
additional review, as well as real errors in the code.
What can be said about the quality of the analyzer work, in comparison with 2015? The first thing that
caught our eye is that there were none of the warnings present, which had been described in the
previous article. It seems (or at least we want to believe) that the Umbraco developers paid attention to
Andrey's article, and fixed the errors described in it. Although the project is of course in continuous
development, and the bugs could be fixed anyway, during daily work. Anyway - there are almost no old
mistakes. Still, there are a lot of new ones! I'll go through the most interesting bugs here.
The analysis results
Potential division by zero
PVS-Studio warning: V3064 Potential division by zero. Consider inspecting denominator
'maxWidthHeight'. ImageHelper.cs 154
PVS-Studio warning: V3064 Potential division by zero. Consider inspecting denominator
'maxWidthHeight'. ImageHelper.cs 155
private static ResizedImage GenerateThumbnail(....)
{
....
if (maxWidthHeight >= 0)
{
var fx = (float)image.Size.Width / maxWidthHeight; // <=
var fy = (float)image.Size.Height / maxWidthHeight; // <=
....
}
....
}
The provided code fragment has two possible errors, although the second one will never be executed.
The condition of the if block allows the maxWidthHeight variable to be equal to zero, which acts as a
divisor inside the block. In general, this code can work normally for quite a long period of time, and this
is the biggest danger in it. Looking at the name of the maxWidthHeight, we can conclude that it's value
is most likely not equal to zero. Well, what if it is zero at some point of the execution? The correct
version of this construction is as follows:
private static ResizedImage GenerateThumbnail(....)
{
....
if (maxWidthHeight > 0)
{
var fx = (float)image.Size.Width / maxWidthHeight;
var fy = (float)image.Size.Height / maxWidthHeight;
....
}
....
}
The case where the variable maxWidthHeight is zero, should be inspected separately.
A vexatious typo
PVS-Studio warning: V3080 Possible null dereference. Consider inspecting 'context.Request'.
StateHelper.cs 369
public static bool HasCookies
{
get
{
var context = HttpContext;
return context != null && context.Request != null & // <=
context.Request.Cookies != null &&
context.Response != null &&
context.Response.Cookies != null;
}
}
There is a typo: the & operator is used instead of &&. The condition context.Request.Cookies != null will
be checked regardless of the result from the check of the previous condition context.Request != null.
This will inevitably lead to access by a zero reference in case the variable context.Request is null. The
correct version of this construction is as follows:
public static bool HasCookies
{
get
{
var context = HttpContext;
return context != null && context.Request != null &&
context.Request.Cookies != null &&
context.Response != null &&
context.Response.Cookies != null;
}
}
Untimely verification against null
PVS-Studio warning: V3027 The variable 'rootDoc' was utilized in the logical expression before it was
verified against null in the same logical expression. publishRootDocument.cs 34
public bool Execute(....)
{
....
if (rootDoc.Text.Trim() == documentName.Trim() && // <=
rootDoc != null && rootDoc.ContentType != null)
....
}
The variable rootDoc is verified against null after the accessing via rootDoc.Text. The correct version of
this construction is as follows:
public bool Execute(....)
{
....
if (rootDoc != null &&
rootDoc.Text.Trim() == documentName.Trim() &&
rootDoc.ContentType != null)
....
}
A negative character index in the string
PVS-Studio warning: V3057 The 'Substring' function could receive the '-1' value while non-negative
value is expected. Inspect the second argument. ContentExtensions.cs 82
internal static CultureInfo GetCulture(....)
{
....
var pos = route.IndexOf('/');
domain = pos == 0
? null
: domainHelper.DomainForNode(
int.Parse(route.Substring(0, pos)), current) // <=
.UmbracoDomain;
....
}
In the route string the program searches for the '/' character, after which the the variable is assigned
with the pos variable. The author took into account the possibility of a character in the beginning of the
string (pos == 0), but didn't take into account the possibility of its absence: in this case the variable pos
will get the value -1. This will cause an exception upon the subsequent usage of the pos variable to
extract the substring route.Substring(0, pos). The correct version of this construction is as follows:
internal static CultureInfo GetCulture(....)
{
....
var pos = route.IndexOf('/');
domain = (pos <= 0)
? null
: domainHelper.DomainForNode(
int.Parse(route.Substring(0, pos)), current)
.UmbracoDomain;
....
}
Similar warnings:
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the first argument. DefaultUrlProvider.cs 81
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. DefaultUrlProvider.cs 84
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the first argument. DefaultUrlProvider.cs 126
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. DefaultUrlProvider.cs 127
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the first argument. PublishedContentCache.cs 147
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. PublishedContentCache.cs 148
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. ContentFinderByNiceUrlAndTemplate.cs 35
 V3057 The 'Substring' function could receive the '-9' value while non-negative value is expected.
Inspect the second argument. requestModule.cs 187
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. Action.cs 134
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the first argument. LegacyShortStringHelper.cs 130
 V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected.
Inspect the second argument. StringExtensions.cs 573
Time is money
PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is
expected. Inspect the second argument. DateTimeExtensions.cs 24
PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is
expected. Inspect the third argument. DateTimeExtensions.cs 24
PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is
expected. Inspect the third argument. DateTimeExtensions.cs 26
public static DateTime TruncateTo(this DateTime dt,
DateTruncate truncateTo)
{
if (truncateTo == DateTruncate.Year)
return new DateTime(dt.Year, 0, 0); // <= x2
if (truncateTo == DateTruncate.Month)
return new DateTime(dt.Year, dt.Month, 0); // <=
....
}
This small snippet also contains error 3, immediately detected by diagnostic rule V3057. All the errors
related to incorrect initialization of the object of the DateTime class, whose constructor is as follows:
public DateTime(int year, int month, int day). At the same time, the parameters year, month, and day
cannot take values <1. Otherwise, an ArgumentOutOfRangeException will be thrown. The correct
version of this construction is as follows:
public static DateTime TruncateTo(this DateTime dt,
DateTruncate truncateTo)
{
if (truncateTo == DateTruncate.Year)
return new DateTime(dt.Year, 1, 1);
if (truncateTo == DateTruncate.Month)
return new DateTime(dt.Year, dt.Month, 1);
....
}
Erroneous condition
PVS-Studio warning: V3125 The 'ct' object was used after it was verified against null. Check lines: 171,
163. ContentTypeControllerBase.cs 171
protected TContentType PerformPostSave<....>(....)
{
var ctId = Convert.ToInt32(....);
....
if (ctId > 0 && ct == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
....
if ((....) &&
(ctId == 0 || ct.Alias != contentTypeSave.Alias)) // <=
....
}
There is the possibility of access by the null reference because of the condition (ctId > 0 && ct == null) in
this code fragment. The exception HttpResponseException will be thrown only if both parts of the
condition are true at the same time. In the case that the ctld variable is <= 0, the work will be continued
anyway regardless of the value of the ct variable. The error needs to fixed in the second condition,
where the ct is used. The correct version of this construction is as follows
protected TContentType PerformPostSave<....>(....)
{
var ctId = Convert.ToInt32(....);
....
if (ctId > 0 && ct == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
....
if ((....) &&
(ctId == 0 ||
(ct != null && ct.Alias != contentTypeSave.Alias)))
....
}
Similar warnings:
 V3125 The '_repo' object was used after it was verified against null. Check lines: 104, 78.
Installer.aspx.cs 104
 V3125 The 'docRequest.RoutingContext.UmbracoContext' object was used after it was verified
against null. Check lines: 57, 39. ContentFinderByIdPath.cs 57
 V3125 The 'User' object was used after it was verified against null. Check lines: 90, 80. config.cs
90
 V3125 The '_repo' object was used after it was verified against null. Check lines: 254, 247.
installedPackage.aspx.cs 254
 V3125 The 'node.NiceUrl' object was used after it was verified against null. Check lines: 917,
912. NodeExtensions.cs 917
 V3125 The 'dst' object was used after it was verified against null. Check lines: 58, 55.
DataEditorSetting.cs 58
 V3125 The 'result' object was used after it was verified against null. Check lines: 199, 188.
DefaultPreValueEditor.cs 199
 V3125 The 'result' object was used after it was verified against null. Check lines: 241, 230.
usercontrolPrevalueEditor.cs 241
An error in the format string
PVS-Studio warning: V3025 Incorrect format. A different number of format items is expected while
calling 'Format' function. Format items not used: {1}. Arguments not used: 1st.
HtmlHelperRenderExtensions.cs 938
public static IHtmlString EnableCanvasDesigner(....)
{
....
string noPreviewLinks = @"<link href=""{1}"" type=
""text/css"" rel=""stylesheet"
" data-title=""canvasdesignerCss"" />";
....
if (....)
result = string.Format(noPreviewLinks, cssPath) + // <=
Environment.NewLine;
....
}
The format string noPreviewLinks doesn't have a specifier '{0}' for the first argument cssPath of the
string.Format method. The result of this code execution will be that we'll get an exception. The correct
version of this construction is as follows:
public static IHtmlString EnableCanvasDesigner(....)
{
....
string noPreviewLinks = @"<link href=""{0}"" type=
""text/css"" rel=""stylesheet"
" data-title=""canvasdesignerCss"" />";
....
if (....)
result = string.Format(noPreviewLinks, cssPath) +
Environment.NewLine;
....
}
Similar warnings:
 V3025 Incorrect format. A different number of format items is expected while calling 'Format'
function. Format items not used: {1}. Arguments not used: 1st. HtmlHelperRenderExtensions.cs
946
 V3025 Incorrect format. A different number of format items is expected while calling 'Format'
function. Arguments not used: path. requestModule.cs 204
 V3025 Incorrect format. A different number of format items is expected while calling 'Format'
function. Arguments not used: Alias.Replace(" ", ""). Template.cs 382
 V3025 Incorrect format. A different number of format items is expected while calling 'Format'
function. Arguments not used: Alias.Replace(" ", ""). Template.cs 387
 V3025 Incorrect format. A different number of format items is expected while calling 'Format'
function. Arguments not used: this.Value.ClientID. SliderPrevalueEditor.cs 221
Untimely verification against null. Again
PVS-Studio warning: V3095 The 'dataset' object was used before it was verified against null. Check lines:
48, 49. ImageCropperBaseExtensions.cs 48
internal static ImageCropData GetCrop(....)
{
var imageCropDatas = dataset.ToArray(); // <=
if (dataset == null || imageCropDatas.Any() == false)
return null;
....
}
Unlike V3027 diagnostic - where the untimely verification against null was found within a single
condition - here we are dealing with an attempt to access the null reference in a different statement.
The variable dataset is converted to array first, and only then is verified against null. The correct version
of this construction is as follows:
internal static ImageCropData GetCrop(....)
{
var imageCropDatas = dataset?.ToArray();
if (imageCropDatas == null || !imageCropDatas.Any())
return null;
....
}
Similar warnings:
 V3095 The 'display.PropertyEditor' object was used before it was verified against null. Check
lines: 30, 43. ContentPropertyDisplayConverter.cs 30
 V3095 The 'typedSource' object was used before it was verified against null. Check lines: 164,
198. DynamicQueryable.cs 164
 V3095 The 'attempt.Result' object was used before it was verified against null. Check lines: 90,
113. DynamicPublishedContent.cs 90
 V3095 The 'actionExecutedContext' object was used before it was verified against null. Check
lines: 47, 76. FileUploadCleanupFilterAttribute.cs 47
 V3095 The 'type' object was used before it was verified against null. Check lines: 92, 96.
assemblyBrowser.aspx.cs 92
 V3095 The 'httpContext' object was used before it was verified against null. Check lines: 235,
237. UmbracoContext.cs 235
 V3095 The 'dst' object was used before it was verified against null. Check lines: 53, 55.
DataEditorSetting.cs 53
 V3095 The '_val' object was used before it was verified against null. Check lines: 46, 55.
CheckBoxList.cs 46
 V3095 The '_val' object was used before it was verified against null. Check lines: 47, 54.
ListBoxMultiple.cs 47
 V3095 The 'databaseSettings.ConnectionString' object was used before it was verified against
null. Check lines: 737, 749. DatabaseContext.cs 737
 V3095 The 'path' object was used before it was verified against null. Check lines: 101, 112.
IOHelper.cs 101
A logic error
PVS-Studio warning: V3022 Expression 'name != "Min" || name != "Max"' is always true. Probably the
'&&' operator should be used here. DynamicPublishedContentList.cs 415
private object Aggregate(....)
{
....
if (name != "Min" || name != "Max") // <=
{
throw new ArgumentException(
"Can only use aggregate min or max methods on properties
which are datetime");
}
....
}
As can be seen in the message of the exception, the name variable can only take one of the values
"Min", or "Max". At the same time, the condition of the exception should be simultaneous unequal to
the name variable "Min" and "Max". But in this fragment the exception will be thrown regardless of the
value of name. The correct version of this construction is as follows:
private object Aggregate(....)
{
....
if (name != "Min" && name != "Max")
{
throw new ArgumentException(
"Can only use aggregate min or max methods on properties
which are datetime");
}
....
}
In the Umbraco code, the analyzer found 32 more potentially dangerous constructions (although they
may just be redundant checks). Here are some of them:
 V3022 Expression 'macro == null' is always false. MacroController.cs 91
 V3022 Expression 'p.Value == null' is always false. ImageCropperPropertyEditor.cs 216
 V3022 Expression 'loginPageObj != null' is always true. ProtectPage.aspx.cs 93
 V3022 Expression 'dictionaryItem != null' is always true. TranslateTreeNames.cs 19
 V3022 Expression '!IsPostBack' is always true. EditUser.aspx.cs 431
 V3022 Expression 'result.View != null' is always false. ControllerExtensions.cs 129
 V3022 Expression
'string.IsNullOrEmpty(UmbracoSettings.TEMP_FRIENDLY_XML_CHILD_CONTAINER_NODENAME
) == false' is always false. NotFoundHandlers.cs 128
 V3022 Expression 'mem != null' is always true. ViewMembers.aspx.cs 96
 V3022 Expression 'dtd != null' is always true. installedPackage.aspx.cs 213
 V3022 Expression 'jsonReader.TokenType == JSONToken.EndArray && jsonReader.Value == null'
is always false. JSON.cs 263
A strange loop condition
PVS-Studio warning: V3022 Expression '!stop' is always true. template.cs 229
public Control parseStringBuilder(....)
{
....
bool stop = false;
....
while (!stop) // <=
{
....
}
....
}
Another suspicious construction, detected by the V3022 diagnostic. The variable stop isn't used inside
the while block. The block has quite a large code fragment, about 140 lines of code, that's why I won't
cite it here. Here is the result of searching for the stop variable:
Most likely, it's not an infinite loop, as we can see a break here, as well as the exception handling blocks.
Nevertheless, the loop looks very strange, and may contain a potential error.
Infinite recursion
PVS-Studio warning: V3110 Possible infinite recursion inside 'Render' method. MenuSplitButton.cs 30
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.Write("</div>");
base.Render(writer);
this.Render(writer); // <=
writer.Write("<div class='btn-group>");
}
Apparently, this code fragment has a bug caused by an infinite recursion. After the call of the Render
method of the base class, there is a recursive call of the overloaded Render method "by the analogy".
Perhaps, the method this.Render should contain some condition of the exit from the recursion.
However, it's hard to make a clear conclusion as to what the correct variant of this construction should
be.
Conclusion
So, the recheck of the Umbraco project showed significant progress in PVS-Studio, in searching for
potentially dangerous and erroneous constructs in C# code. The analyzer has proven its effectiveness
once again. Of course, projects should not be checked once a year, because the maximum effect of
static analysis is achieved only through regular use. This allows the fixing of bugs effectively, and in good
time, without letting them get to the build system, and to the end-users.
Use static analysis! We added the possibility to use our analyzer for free so that everybody could do
that. Good luck in the battle against errors and bugless code!

More Related Content

PDF
Dusting the globe: analysis of NASA World Wind project
PDF
A Spin-off: CryEngine 3 SDK Checked with CppCat
PDF
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
PDF
A few words about OpenSSL
PDF
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
PDF
PVS-Studio vs Chromium - Continuation
PDF
Linux version of PVS-Studio couldn't help checking CodeLite
PDF
Checking OpenCV with PVS-Studio
Dusting the globe: analysis of NASA World Wind project
A Spin-off: CryEngine 3 SDK Checked with CppCat
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
A few words about OpenSSL
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
PVS-Studio vs Chromium - Continuation
Linux version of PVS-Studio couldn't help checking CodeLite
Checking OpenCV with PVS-Studio

What's hot (20)

PDF
CppCat Static Analyzer Review
PDF
Analysis of the Trans-Proteomic Pipeline (TPP) project
PDF
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
PDF
Top 10 C# projects errors found in 2016
PDF
LibRaw, Coverity SCAN, PVS-Studio
PDF
Going On with the Check of Geant4
PDF
A fresh eye on Oracle VM VirtualBox
PDF
Checking VirtualDub
PDF
C++ boot camp part 1/2
PDF
The Unicorn's Travel to the Microcosm
PDF
Picking Mushrooms after Cppcheck
PDF
Checking 7-Zip with PVS-Studio analyzer
PDF
Checking Clang 11 with PVS-Studio
PDF
100 bugs in Open Source C/C++ projects
PDF
A Boring Article About a Check of the OpenSSL Project
PDF
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
PDF
Checking the code of Valgrind dynamic analyzer by a static analyzer
PDF
Tesseract. Recognizing Errors in Recognition Software
PDF
Checking Bitcoin
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
CppCat Static Analyzer Review
Analysis of the Trans-Proteomic Pipeline (TPP) project
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Top 10 C# projects errors found in 2016
LibRaw, Coverity SCAN, PVS-Studio
Going On with the Check of Geant4
A fresh eye on Oracle VM VirtualBox
Checking VirtualDub
C++ boot camp part 1/2
The Unicorn's Travel to the Microcosm
Picking Mushrooms after Cppcheck
Checking 7-Zip with PVS-Studio analyzer
Checking Clang 11 with PVS-Studio
100 bugs in Open Source C/C++ projects
A Boring Article About a Check of the OpenSSL Project
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
Checking the code of Valgrind dynamic analyzer by a static analyzer
Tesseract. Recognizing Errors in Recognition Software
Checking Bitcoin
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Ad

Viewers also liked (11)

PDF
Pet Role Trust Flyer
PDF
Seminario 11 Aspectos nutricionales en la insuficiencia renal
PDF
HonoringOurFreedom_East (1)
PPTX
Biodegradable Materials, Healthy shoes
PPTX
Trastornos Mentales
PPTX
Bangi kajang 30 tahun akan datang
PDF
Anexo primera infancia
DOCX
research project
PDF
Bio-Data of Srinivas
PPTX
Autoevaluación
PPT
Auditoria de sistemas
Pet Role Trust Flyer
Seminario 11 Aspectos nutricionales en la insuficiencia renal
HonoringOurFreedom_East (1)
Biodegradable Materials, Healthy shoes
Trastornos Mentales
Bangi kajang 30 tahun akan datang
Anexo primera infancia
research project
Bio-Data of Srinivas
Autoevaluación
Auditoria de sistemas
Ad

Similar to Re-analysis of Umbraco code (20)

PDF
Brief analysis of Media Portal 2 bugs
PDF
Source code of WPF samples by Microsoft was checked
PDF
Diving into VS 2015 Day2
PDF
The First C# Project Analyzed
PDF
Checking the Source Code of FlashDevelop with PVS-Studio
PDF
Rechecking SharpDevelop: Any New Bugs?
PDF
Looking for Bugs in MonoDevelop
PDF
Analysis of bugs in Orchard CMS
PDF
Searching for bugs in Mono: there are hundreds of them!
PDF
Diving into VS 2015 Day1
PDF
Analysis of Microsoft Code Contracts
PDF
PVS-Studio in 2021 - Error Examples
PDF
Discussing Errors in Unity3D's Open-Source Components
PDF
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PDF
Static analysis and ROI
PDF
Static analysis and ROI
PDF
We continue checking Microsoft projects: analysis of PowerShell
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PDF
Sony C#/.NET component set analysis
PDF
The Little Unicorn That Could
Brief analysis of Media Portal 2 bugs
Source code of WPF samples by Microsoft was checked
Diving into VS 2015 Day2
The First C# Project Analyzed
Checking the Source Code of FlashDevelop with PVS-Studio
Rechecking SharpDevelop: Any New Bugs?
Looking for Bugs in MonoDevelop
Analysis of bugs in Orchard CMS
Searching for bugs in Mono: there are hundreds of them!
Diving into VS 2015 Day1
Analysis of Microsoft Code Contracts
PVS-Studio in 2021 - Error Examples
Discussing Errors in Unity3D's Open-Source Components
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
Static analysis and ROI
Static analysis and ROI
We continue checking Microsoft projects: analysis of PowerShell
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Sony C#/.NET component set analysis
The Little Unicorn That Could

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
medical staffing services at VALiNTRY
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
Transform Your Business with a Software ERP System
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Digital Strategies for Manufacturing Companies
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PPTX
Computer Software and OS of computer science of grade 11.pptx
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Softaken Excel to vCard Converter Software.pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Reimagine Home Health with the Power of Agentic AI​
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
medical staffing services at VALiNTRY
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PTS Company Brochure 2025 (1).pdf.......
Transform Your Business with a Software ERP System
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Which alternative to Crystal Reports is best for small or large businesses.pdf
How to Migrate SBCGlobal Email to Yahoo Easily
Digital Strategies for Manufacturing Companies
2025 Textile ERP Trends: SAP, Odoo & Oracle
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Design an Analysis of Algorithms I-SECS-1021-03
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Computer Software and OS of computer science of grade 11.pptx
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf

Re-analysis of Umbraco code

  • 1. Re-analysis of Umbraco code Author: Sergey Khrenov Date: 16.12.2016 Time passes inexorably. It feels that just recently we announced the release of the C# static code analyzer, checked the first projects, and started writing articles about it. But a whole year has passed since that moment. It was a year of painstaking and hard work on diagnostic improvements, adding new diagnostic rules, gathering statistics on false positives and eliminating their causes, communicating with users, and tackling a great deal of other issues. It was a year of both small and large successes on this hard, but incredibly interesting, path we have chosen. Now it's time for a re-analysis of the Umbraco project that we checked right after the release of our C# analyzer a year ago. Introduction The first article about the Umbraco analysis was written by my colleague Andrey Karpov. This year the project continued development, and so far it contains about 3340 files with the extension ".cs", which is approximately 425 KLOC (at the time of the first check, the project had 3200 files with the extension ".cs", and 400 KLOC respectively). On the first check, the analyzer found a relatively small number of errors, which were nevertheless quite interesting to write an article on, and to draw first conclusions about the work of the C# analyzer from. It is far more interesting doing the check now, when the analyzer has obtained dozens of new diagnostic rules, and improved it's mechanisms of searching for bugs; it's also quite amusing to compare the results of the present time check with the one we did a year ago. To do the analysis, I used the latest version of Umbraco source code, which is also available at GitHub, and also the latest version of PVS-Studio 6.11. In the results of the check, we got 508 warnings. 71 warnings were first level, 358 - second level, 79 - third level.
  • 2. The overall coefficient of issues density (the number of warnings per KLOC) was 1.12. This is a good indicator which corresponds to approximately one warning per a thousand lines of code. But warnings do not necessarily mean real errors. It's normal for any static analyzer to have a certain percentage of false positives. Quite often, the warnings look like real bugs, but later after inspection, it turns out that it's not so. Therefore, I won't be discussing the low level warnings, as the percentage of false positives is usually quite high there. I have reviewed the warnings issued by PVS-Studio, and detected approximately 56% false positives at High and Meduim levels. The remaining warnings contain quite suspicious constructs that require additional review, as well as real errors in the code. What can be said about the quality of the analyzer work, in comparison with 2015? The first thing that caught our eye is that there were none of the warnings present, which had been described in the previous article. It seems (or at least we want to believe) that the Umbraco developers paid attention to Andrey's article, and fixed the errors described in it. Although the project is of course in continuous development, and the bugs could be fixed anyway, during daily work. Anyway - there are almost no old mistakes. Still, there are a lot of new ones! I'll go through the most interesting bugs here. The analysis results Potential division by zero PVS-Studio warning: V3064 Potential division by zero. Consider inspecting denominator 'maxWidthHeight'. ImageHelper.cs 154 PVS-Studio warning: V3064 Potential division by zero. Consider inspecting denominator 'maxWidthHeight'. ImageHelper.cs 155 private static ResizedImage GenerateThumbnail(....) { .... if (maxWidthHeight >= 0)
  • 3. { var fx = (float)image.Size.Width / maxWidthHeight; // <= var fy = (float)image.Size.Height / maxWidthHeight; // <= .... } .... } The provided code fragment has two possible errors, although the second one will never be executed. The condition of the if block allows the maxWidthHeight variable to be equal to zero, which acts as a divisor inside the block. In general, this code can work normally for quite a long period of time, and this is the biggest danger in it. Looking at the name of the maxWidthHeight, we can conclude that it's value is most likely not equal to zero. Well, what if it is zero at some point of the execution? The correct version of this construction is as follows: private static ResizedImage GenerateThumbnail(....) { .... if (maxWidthHeight > 0) { var fx = (float)image.Size.Width / maxWidthHeight; var fy = (float)image.Size.Height / maxWidthHeight; .... } .... } The case where the variable maxWidthHeight is zero, should be inspected separately. A vexatious typo PVS-Studio warning: V3080 Possible null dereference. Consider inspecting 'context.Request'. StateHelper.cs 369 public static bool HasCookies { get { var context = HttpContext; return context != null && context.Request != null & // <= context.Request.Cookies != null && context.Response != null &&
  • 4. context.Response.Cookies != null; } } There is a typo: the & operator is used instead of &&. The condition context.Request.Cookies != null will be checked regardless of the result from the check of the previous condition context.Request != null. This will inevitably lead to access by a zero reference in case the variable context.Request is null. The correct version of this construction is as follows: public static bool HasCookies { get { var context = HttpContext; return context != null && context.Request != null && context.Request.Cookies != null && context.Response != null && context.Response.Cookies != null; } } Untimely verification against null PVS-Studio warning: V3027 The variable 'rootDoc' was utilized in the logical expression before it was verified against null in the same logical expression. publishRootDocument.cs 34 public bool Execute(....) { .... if (rootDoc.Text.Trim() == documentName.Trim() && // <= rootDoc != null && rootDoc.ContentType != null) .... } The variable rootDoc is verified against null after the accessing via rootDoc.Text. The correct version of this construction is as follows: public bool Execute(....) { .... if (rootDoc != null && rootDoc.Text.Trim() == documentName.Trim() &&
  • 5. rootDoc.ContentType != null) .... } A negative character index in the string PVS-Studio warning: V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. ContentExtensions.cs 82 internal static CultureInfo GetCulture(....) { .... var pos = route.IndexOf('/'); domain = pos == 0 ? null : domainHelper.DomainForNode( int.Parse(route.Substring(0, pos)), current) // <= .UmbracoDomain; .... } In the route string the program searches for the '/' character, after which the the variable is assigned with the pos variable. The author took into account the possibility of a character in the beginning of the string (pos == 0), but didn't take into account the possibility of its absence: in this case the variable pos will get the value -1. This will cause an exception upon the subsequent usage of the pos variable to extract the substring route.Substring(0, pos). The correct version of this construction is as follows: internal static CultureInfo GetCulture(....) { .... var pos = route.IndexOf('/'); domain = (pos <= 0) ? null : domainHelper.DomainForNode( int.Parse(route.Substring(0, pos)), current) .UmbracoDomain; .... } Similar warnings:  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the first argument. DefaultUrlProvider.cs 81
  • 6.  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. DefaultUrlProvider.cs 84  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the first argument. DefaultUrlProvider.cs 126  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. DefaultUrlProvider.cs 127  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the first argument. PublishedContentCache.cs 147  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. PublishedContentCache.cs 148  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. ContentFinderByNiceUrlAndTemplate.cs 35  V3057 The 'Substring' function could receive the '-9' value while non-negative value is expected. Inspect the second argument. requestModule.cs 187  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. Action.cs 134  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the first argument. LegacyShortStringHelper.cs 130  V3057 The 'Substring' function could receive the '-1' value while non-negative value is expected. Inspect the second argument. StringExtensions.cs 573 Time is money PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is expected. Inspect the second argument. DateTimeExtensions.cs 24 PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is expected. Inspect the third argument. DateTimeExtensions.cs 24 PVS-Studio warning: V3057 The 'DateTime' constructor receives the '0' value while positive value is expected. Inspect the third argument. DateTimeExtensions.cs 26 public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo) { if (truncateTo == DateTruncate.Year) return new DateTime(dt.Year, 0, 0); // <= x2 if (truncateTo == DateTruncate.Month) return new DateTime(dt.Year, dt.Month, 0); // <= .... } This small snippet also contains error 3, immediately detected by diagnostic rule V3057. All the errors related to incorrect initialization of the object of the DateTime class, whose constructor is as follows: public DateTime(int year, int month, int day). At the same time, the parameters year, month, and day cannot take values <1. Otherwise, an ArgumentOutOfRangeException will be thrown. The correct version of this construction is as follows: public static DateTime TruncateTo(this DateTime dt,
  • 7. DateTruncate truncateTo) { if (truncateTo == DateTruncate.Year) return new DateTime(dt.Year, 1, 1); if (truncateTo == DateTruncate.Month) return new DateTime(dt.Year, dt.Month, 1); .... } Erroneous condition PVS-Studio warning: V3125 The 'ct' object was used after it was verified against null. Check lines: 171, 163. ContentTypeControllerBase.cs 171 protected TContentType PerformPostSave<....>(....) { var ctId = Convert.ToInt32(....); .... if (ctId > 0 && ct == null) throw new HttpResponseException(HttpStatusCode.NotFound); .... if ((....) && (ctId == 0 || ct.Alias != contentTypeSave.Alias)) // <= .... } There is the possibility of access by the null reference because of the condition (ctId > 0 && ct == null) in this code fragment. The exception HttpResponseException will be thrown only if both parts of the condition are true at the same time. In the case that the ctld variable is <= 0, the work will be continued anyway regardless of the value of the ct variable. The error needs to fixed in the second condition, where the ct is used. The correct version of this construction is as follows protected TContentType PerformPostSave<....>(....) { var ctId = Convert.ToInt32(....); .... if (ctId > 0 && ct == null) throw new HttpResponseException(HttpStatusCode.NotFound); .... if ((....) &&
  • 8. (ctId == 0 || (ct != null && ct.Alias != contentTypeSave.Alias))) .... } Similar warnings:  V3125 The '_repo' object was used after it was verified against null. Check lines: 104, 78. Installer.aspx.cs 104  V3125 The 'docRequest.RoutingContext.UmbracoContext' object was used after it was verified against null. Check lines: 57, 39. ContentFinderByIdPath.cs 57  V3125 The 'User' object was used after it was verified against null. Check lines: 90, 80. config.cs 90  V3125 The '_repo' object was used after it was verified against null. Check lines: 254, 247. installedPackage.aspx.cs 254  V3125 The 'node.NiceUrl' object was used after it was verified against null. Check lines: 917, 912. NodeExtensions.cs 917  V3125 The 'dst' object was used after it was verified against null. Check lines: 58, 55. DataEditorSetting.cs 58  V3125 The 'result' object was used after it was verified against null. Check lines: 199, 188. DefaultPreValueEditor.cs 199  V3125 The 'result' object was used after it was verified against null. Check lines: 241, 230. usercontrolPrevalueEditor.cs 241 An error in the format string PVS-Studio warning: V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Format items not used: {1}. Arguments not used: 1st. HtmlHelperRenderExtensions.cs 938 public static IHtmlString EnableCanvasDesigner(....) { .... string noPreviewLinks = @"<link href=""{1}"" type= ""text/css"" rel=""stylesheet" " data-title=""canvasdesignerCss"" />"; .... if (....) result = string.Format(noPreviewLinks, cssPath) + // <= Environment.NewLine; .... } The format string noPreviewLinks doesn't have a specifier '{0}' for the first argument cssPath of the string.Format method. The result of this code execution will be that we'll get an exception. The correct version of this construction is as follows:
  • 9. public static IHtmlString EnableCanvasDesigner(....) { .... string noPreviewLinks = @"<link href=""{0}"" type= ""text/css"" rel=""stylesheet" " data-title=""canvasdesignerCss"" />"; .... if (....) result = string.Format(noPreviewLinks, cssPath) + Environment.NewLine; .... } Similar warnings:  V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Format items not used: {1}. Arguments not used: 1st. HtmlHelperRenderExtensions.cs 946  V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Arguments not used: path. requestModule.cs 204  V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Arguments not used: Alias.Replace(" ", ""). Template.cs 382  V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Arguments not used: Alias.Replace(" ", ""). Template.cs 387  V3025 Incorrect format. A different number of format items is expected while calling 'Format' function. Arguments not used: this.Value.ClientID. SliderPrevalueEditor.cs 221 Untimely verification against null. Again PVS-Studio warning: V3095 The 'dataset' object was used before it was verified against null. Check lines: 48, 49. ImageCropperBaseExtensions.cs 48 internal static ImageCropData GetCrop(....) { var imageCropDatas = dataset.ToArray(); // <= if (dataset == null || imageCropDatas.Any() == false) return null; .... } Unlike V3027 diagnostic - where the untimely verification against null was found within a single condition - here we are dealing with an attempt to access the null reference in a different statement. The variable dataset is converted to array first, and only then is verified against null. The correct version of this construction is as follows:
  • 10. internal static ImageCropData GetCrop(....) { var imageCropDatas = dataset?.ToArray(); if (imageCropDatas == null || !imageCropDatas.Any()) return null; .... } Similar warnings:  V3095 The 'display.PropertyEditor' object was used before it was verified against null. Check lines: 30, 43. ContentPropertyDisplayConverter.cs 30  V3095 The 'typedSource' object was used before it was verified against null. Check lines: 164, 198. DynamicQueryable.cs 164  V3095 The 'attempt.Result' object was used before it was verified against null. Check lines: 90, 113. DynamicPublishedContent.cs 90  V3095 The 'actionExecutedContext' object was used before it was verified against null. Check lines: 47, 76. FileUploadCleanupFilterAttribute.cs 47  V3095 The 'type' object was used before it was verified against null. Check lines: 92, 96. assemblyBrowser.aspx.cs 92  V3095 The 'httpContext' object was used before it was verified against null. Check lines: 235, 237. UmbracoContext.cs 235  V3095 The 'dst' object was used before it was verified against null. Check lines: 53, 55. DataEditorSetting.cs 53  V3095 The '_val' object was used before it was verified against null. Check lines: 46, 55. CheckBoxList.cs 46  V3095 The '_val' object was used before it was verified against null. Check lines: 47, 54. ListBoxMultiple.cs 47  V3095 The 'databaseSettings.ConnectionString' object was used before it was verified against null. Check lines: 737, 749. DatabaseContext.cs 737  V3095 The 'path' object was used before it was verified against null. Check lines: 101, 112. IOHelper.cs 101 A logic error PVS-Studio warning: V3022 Expression 'name != "Min" || name != "Max"' is always true. Probably the '&&' operator should be used here. DynamicPublishedContentList.cs 415 private object Aggregate(....) { .... if (name != "Min" || name != "Max") // <= { throw new ArgumentException( "Can only use aggregate min or max methods on properties which are datetime");
  • 11. } .... } As can be seen in the message of the exception, the name variable can only take one of the values "Min", or "Max". At the same time, the condition of the exception should be simultaneous unequal to the name variable "Min" and "Max". But in this fragment the exception will be thrown regardless of the value of name. The correct version of this construction is as follows: private object Aggregate(....) { .... if (name != "Min" && name != "Max") { throw new ArgumentException( "Can only use aggregate min or max methods on properties which are datetime"); } .... } In the Umbraco code, the analyzer found 32 more potentially dangerous constructions (although they may just be redundant checks). Here are some of them:  V3022 Expression 'macro == null' is always false. MacroController.cs 91  V3022 Expression 'p.Value == null' is always false. ImageCropperPropertyEditor.cs 216  V3022 Expression 'loginPageObj != null' is always true. ProtectPage.aspx.cs 93  V3022 Expression 'dictionaryItem != null' is always true. TranslateTreeNames.cs 19  V3022 Expression '!IsPostBack' is always true. EditUser.aspx.cs 431  V3022 Expression 'result.View != null' is always false. ControllerExtensions.cs 129  V3022 Expression 'string.IsNullOrEmpty(UmbracoSettings.TEMP_FRIENDLY_XML_CHILD_CONTAINER_NODENAME ) == false' is always false. NotFoundHandlers.cs 128  V3022 Expression 'mem != null' is always true. ViewMembers.aspx.cs 96  V3022 Expression 'dtd != null' is always true. installedPackage.aspx.cs 213  V3022 Expression 'jsonReader.TokenType == JSONToken.EndArray && jsonReader.Value == null' is always false. JSON.cs 263 A strange loop condition PVS-Studio warning: V3022 Expression '!stop' is always true. template.cs 229 public Control parseStringBuilder(....) { .... bool stop = false;
  • 12. .... while (!stop) // <= { .... } .... } Another suspicious construction, detected by the V3022 diagnostic. The variable stop isn't used inside the while block. The block has quite a large code fragment, about 140 lines of code, that's why I won't cite it here. Here is the result of searching for the stop variable: Most likely, it's not an infinite loop, as we can see a break here, as well as the exception handling blocks. Nevertheless, the loop looks very strange, and may contain a potential error. Infinite recursion PVS-Studio warning: V3110 Possible infinite recursion inside 'Render' method. MenuSplitButton.cs 30 protected override void Render(System.Web.UI.HtmlTextWriter writer) { writer.Write("</div>"); base.Render(writer); this.Render(writer); // <= writer.Write("<div class='btn-group>"); } Apparently, this code fragment has a bug caused by an infinite recursion. After the call of the Render method of the base class, there is a recursive call of the overloaded Render method "by the analogy". Perhaps, the method this.Render should contain some condition of the exit from the recursion. However, it's hard to make a clear conclusion as to what the correct variant of this construction should be. Conclusion So, the recheck of the Umbraco project showed significant progress in PVS-Studio, in searching for potentially dangerous and erroneous constructs in C# code. The analyzer has proven its effectiveness once again. Of course, projects should not be checked once a year, because the maximum effect of static analysis is achieved only through regular use. This allows the fixing of bugs effectively, and in good time, without letting them get to the build system, and to the end-users.
  • 13. Use static analysis! We added the possibility to use our analyzer for free so that everybody could do that. Good luck in the battle against errors and bugless code!