From 80b8509bfc20015bb178ecbcd80bce348b743076 Mon Sep 17 00:00:00 2001
From: Tom Edwards <109803929+TomEdwardsEnscape@users.noreply.github.com>
Date: Mon, 16 Mar 2026 13:20:32 +0100
Subject: [PATCH] Validate layoutable Thickness properties to block NaN or
infinite values (#20899)
* Validate layoutable Thickness properties to block NaN or infinite values
* Use double.IsFinite
---
src/Avalonia.Base/Layout/Layoutable.cs | 4 ++-
src/Avalonia.Controls/Border.cs | 2 +-
.../Chrome/WindowDrawnDecorations.cs | 4 +--
src/Avalonia.Controls/Decorator.cs | 2 +-
src/Avalonia.Controls/Page/Page.cs | 2 +-
.../BorderTests.cs | 28 ++++++++++++++-----
6 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs
index fedea332b6..e0c316c60a 100644
--- a/src/Avalonia.Base/Layout/Layoutable.cs
+++ b/src/Avalonia.Base/Layout/Layoutable.cs
@@ -113,7 +113,7 @@ namespace Avalonia.Layout
/// Defines the property.
///
public static readonly StyledProperty MarginProperty =
- AvaloniaProperty.Register(nameof(Margin));
+ AvaloniaProperty.Register(nameof(Margin), validate: ValidateThickness);
///
/// Defines the property.
@@ -161,6 +161,8 @@ namespace Avalonia.Layout
private static bool ValidateMinimumDimension(double value) => !double.IsPositiveInfinity(value) && ValidateMaximumDimension(value);
private static bool ValidateMaximumDimension(double value) => value >= 0;
+ private static bool ValidateThickness(Thickness value) => double.IsFinite(value.Left) && double.IsFinite(value.Top) && double.IsFinite(value.Right) && double.IsFinite(value.Bottom);
+
///
/// Occurs when the element's effective viewport changes.
///
diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs
index b816858632..29a31d8070 100644
--- a/src/Avalonia.Controls/Border.cs
+++ b/src/Avalonia.Controls/Border.cs
@@ -38,7 +38,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty BorderThicknessProperty =
- AvaloniaProperty.Register(nameof(BorderThickness));
+ AvaloniaProperty.Register(nameof(BorderThickness), validate: MarginProperty.ValidateValue);
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs b/src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs
index ae279d6ab3..48847b5f59 100644
--- a/src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs
+++ b/src/Avalonia.Controls/Chrome/WindowDrawnDecorations.cs
@@ -58,13 +58,13 @@ public class WindowDrawnDecorations : StyledElement
/// Defines the property.
///
public static readonly StyledProperty DefaultFrameThicknessProperty =
- AvaloniaProperty.Register(nameof(DefaultFrameThickness));
+ AvaloniaProperty.Register(nameof(DefaultFrameThickness), validate: Border.BorderThicknessProperty.ValidateValue);
///
/// Defines the property.
///
public static readonly StyledProperty DefaultShadowThicknessProperty =
- AvaloniaProperty.Register(nameof(DefaultShadowThickness));
+ AvaloniaProperty.Register(nameof(DefaultShadowThickness), validate: Border.BorderThicknessProperty.ValidateValue);
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/Decorator.cs b/src/Avalonia.Controls/Decorator.cs
index e62ca0000b..8cd1916718 100644
--- a/src/Avalonia.Controls/Decorator.cs
+++ b/src/Avalonia.Controls/Decorator.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty PaddingProperty =
- AvaloniaProperty.Register(nameof(Padding));
+ AvaloniaProperty.Register(nameof(Padding), validate: MarginProperty.ValidateValue);
///
/// Initializes static members of the class.
diff --git a/src/Avalonia.Controls/Page/Page.cs b/src/Avalonia.Controls/Page/Page.cs
index 601af92580..48b7bd1b0c 100644
--- a/src/Avalonia.Controls/Page/Page.cs
+++ b/src/Avalonia.Controls/Page/Page.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty SafeAreaPaddingProperty =
- AvaloniaProperty.Register(nameof(SafeAreaPadding));
+ AvaloniaProperty.Register(nameof(SafeAreaPadding), validate: PaddingProperty.ValidateValue);
///
/// Defines the property.
diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs
index e31eb08964..df80998b05 100644
--- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs
@@ -1,9 +1,6 @@
+using System;
using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Rendering;
using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -45,14 +42,31 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(6, 6, 0, 0), content.Bounds);
}
-
+
+ [Fact]
+ public void Should_Reject_NaN_Or_Infinite_Thicknesses()
+ {
+ var target = new Border();
+
+ SetValues(target, Layoutable.MarginProperty);
+ SetValues(target, Decorator.PaddingProperty);
+ SetValues(target, Border.BorderThicknessProperty);
+
+ static void SetValues(Border target, AvaloniaProperty property)
+ {
+ Assert.Throws(() => target.SetValue(property, new Thickness(0, 0, 0, double.NaN)));
+ Assert.Throws(() => target.SetValue(property, new Thickness(0, 0, 0, double.PositiveInfinity)));
+ Assert.Throws(() => target.SetValue(property, new Thickness(0, 0, 0, double.NegativeInfinity)));
+ }
+ }
+
public class UseLayoutRounding : ScopedTestBase
{
[Fact]
public void Measure_Rounds_Padding()
{
- var target = new Border
- {
+ var target = new Border
+ {
Padding = new Thickness(1),
Child = new Canvas
{