Skip to content

Commit 3cdbb5d

Browse files
committed
When saving a byte array, throw an exception if the db parameter's size is
set to a value smaller than the length of the array, to prevent silent truncation (NH-3121).
1 parent 9d3acdb commit 3cdbb5d

File tree

6 files changed

+147
-1
lines changed

6 files changed

+147
-1
lines changed

src/NHibernate.Test/DialectTest/MsSql2005DialectFixture.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ public void NH2809()
103103
t = d.GetTypeName(new BinarySqlType(), SqlClientDriver.MaxSizeForLengthLimitedBinary - 1, 0, 0);
104104
Assert.That(t, Is.EqualTo(String.Format("VARBINARY({0})", SqlClientDriver.MaxSizeForLengthLimitedBinary - 1)));
105105

106+
t = d.GetTypeName(new BinarySqlType(), SqlClientDriver.MaxSizeForLengthLimitedBinary, 0, 0);
107+
Assert.That(t, Is.EqualTo(String.Format("VARBINARY({0})", SqlClientDriver.MaxSizeForLengthLimitedBinary)));
108+
106109
t = d.GetTypeName(new BinarySqlType(), SqlClientDriver.MaxSizeForLengthLimitedBinary + 1, 0, 0);
107110
Assert.That(t, Is.EqualTo("VARBINARY(MAX)"));
108111
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Drawing;
3+
using System.Linq;
4+
using System.Reflection;
5+
using NHibernate.Dialect;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.NHSpecificTest.NH3121
9+
{
10+
[TestFixture]
11+
public class Fixture : BugTestCase
12+
{
13+
protected override bool AppliesTo(Dialect.Dialect dialect)
14+
{
15+
return dialect is MsSql2000Dialect; // All MS dialects.
16+
}
17+
18+
// Some notes:
19+
// Mappings for all three properties use either unspecified length (defaulting to 8000 bytes)
20+
// or a length specified to a value smaller than 8001 bytes. This is since for larger values
21+
// the driver will increase the parameter size to int.MaxValue/2.
22+
23+
[Test]
24+
public void ShouldThrowWhenByteArrayTooLong()
25+
{
26+
const int reportSize = 17158;
27+
var random = new Random();
28+
29+
var reportImage = new Byte[reportSize];
30+
random.NextBytes(reportImage);
31+
32+
var report = new Report { UnsizedArray = reportImage };
33+
34+
var ex = Assert.Throws<PropertyValueException>(() => PersistReport(report));
35+
36+
Assert.That(ex.Message, Is.StringContaining("Report.UnsizedArray"));
37+
Assert.That(ex.InnerException, Is.TypeOf<HibernateException>());
38+
Assert.That(ex.InnerException.Message,
39+
Is.EqualTo("The length of the byte[] value exceeds the length configured in the mapping/parameter."));
40+
}
41+
42+
43+
[Test]
44+
public void ShouldThrowWhenImageTooLarge()
45+
{
46+
Assembly assembly = Assembly.Load(MappingsAssembly);
47+
var stream = assembly.GetManifestResourceStream("NHibernate.Test.NHSpecificTest.NH2484.food-photo.jpg");
48+
var image = Bitmap.FromStream(stream);
49+
50+
var report = new Report { Image = image };
51+
52+
var ex = Assert.Throws<PropertyValueException>(() => PersistReport(report));
53+
54+
Assert.That(ex.Message, Is.StringContaining("Report.Image"));
55+
Assert.That(ex.InnerException, Is.TypeOf<HibernateException>());
56+
Assert.That(ex.InnerException.Message,
57+
Is.EqualTo("The length of the byte[] value exceeds the length configured in the mapping/parameter."));
58+
}
59+
60+
61+
[Test]
62+
public void ShouldThrowWhenImageAsISerializableTooLarge()
63+
{
64+
Assembly assembly = Assembly.Load(MappingsAssembly);
65+
var stream = assembly.GetManifestResourceStream("NHibernate.Test.NHSpecificTest.NH2484.food-photo.jpg");
66+
var image = Bitmap.FromStream(stream);
67+
68+
var report = new Report { SerializableImage = image };
69+
70+
var ex = Assert.Throws<PropertyValueException>(() => PersistReport(report));
71+
72+
Assert.That(ex.Message, Is.StringContaining("Report.SerializableImage"));
73+
Assert.That(ex.InnerException, Is.TypeOf<HibernateException>());
74+
Assert.That(ex.InnerException.Message,
75+
Is.EqualTo("The length of the byte[] value exceeds the length configured in the mapping/parameter."));
76+
}
77+
78+
79+
private void PersistReport(Report report)
80+
{
81+
using (var session = OpenSession())
82+
using (session.BeginTransaction())
83+
{
84+
session.Save(report);
85+
session.Flush();
86+
// No commit to avoid DB pollution (test success means we should throw and never insert anyway).
87+
}
88+
}
89+
}
90+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping
3+
xmlns="urn:nhibernate-mapping-2.2"
4+
assembly="NHibernate.Test"
5+
namespace="NHibernate.Test.NHSpecificTest.NH3121">
6+
7+
<class name="Report">
8+
9+
<id name="Id">
10+
<generator class="guid" />
11+
</id>
12+
13+
<property name="UnsizedArray" />
14+
15+
<property name="Image" type="System.Drawing.Image, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
16+
<column name="Image" length="7000" not-null="false" />
17+
</property>
18+
19+
<property name="SerializableImage" type="Serializable(7000)">
20+
<column name="SerializableImage" not-null="false" />
21+
</property>
22+
23+
</class>
24+
25+
</hibernate-mapping>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.Serialization;
5+
using System.Text;
6+
7+
namespace NHibernate.Test.NHSpecificTest.NH3121
8+
{
9+
public class Report
10+
{
11+
public virtual Guid Id { get; set; }
12+
13+
public virtual Byte[] UnsizedArray { get; set; }
14+
public virtual System.Drawing.Image Image { get; set; }
15+
public virtual ISerializable SerializableImage { get; set; }
16+
}
17+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,8 @@
967967
<Compile Include="NHSpecificTest\NH3004\TestSqlClientDriver.cs" />
968968
<Compile Include="NHSpecificTest\NH3074\Fixture.cs" />
969969
<Compile Include="NHSpecificTest\NH3074\Model.cs" />
970+
<Compile Include="NHSpecificTest\NH3121\Fixture.cs" />
971+
<Compile Include="NHSpecificTest\NH3121\Report.cs" />
970972
<Compile Include="NHSpecificTest\NH3126\InvalidCastTestCase.cs" />
971973
<Compile Include="NHSpecificTest\NH3126\Item.cs" />
972974
<Compile Include="NHSpecificTest\NH3126\Property.cs" />
@@ -2834,6 +2836,7 @@
28342836
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
28352837
</ItemGroup>
28362838
<ItemGroup>
2839+
<EmbeddedResource Include="NHSpecificTest\NH3121\Mappings.hbm.xml" />
28372840
<EmbeddedResource Include="NHSpecificTest\NH2789\Mappings.hbm.xml" />
28382841
<EmbeddedResource Include="NHSpecificTest\NH3142\Mappings.hbm.xml" />
28392842
<EmbeddedResource Include="NHSpecificTest\NH3153\SchemaInClass.hbm.xml" />

src/NHibernate/Type/AbstractBinaryType.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,15 @@ public virtual int Compare(object x, object y)
7676
public override void Set(IDbCommand cmd, object value, int index)
7777
{
7878
byte[] internalValue = ToInternalFormat(value);
79-
((IDataParameter)cmd.Parameters[index]).Value = internalValue;
79+
80+
var parameter = (IDbDataParameter)cmd.Parameters[index];
81+
82+
// set the parameter value before the size check, since ODBC changes the size automatically
83+
parameter.Value = internalValue;
84+
85+
// Avoid silent truncation which happens in ADO.NET if the parameter size is set.
86+
if (parameter.Size > 0 && internalValue.Length > parameter.Size)
87+
throw new HibernateException("The length of the byte[] value exceeds the length configured in the mapping/parameter.");
8088
}
8189

8290
public override object Get(IDataReader rs, int index)

0 commit comments

Comments
 (0)