|
27 | 27 |
|
28 | 28 | #include <algorithm> |
29 | 29 | #include <cctype> |
| 30 | +#include <climits> |
30 | 31 | #include <cmath> |
31 | 32 | #include <cstdlib> |
32 | 33 | #include <cstring> |
@@ -848,6 +849,22 @@ static std::string safeFormat(const char *fmt, Args... args) { |
848 | 849 | #pragma clang diagnostic pop |
849 | 850 | #endif |
850 | 851 |
|
| 852 | +// MSBuild evaluates the [MSBuild]::Add/Subtract/Multiply intrinsics in .NET's |
| 853 | +// default unchecked context, so on overflow the result wraps (two's complement). |
| 854 | +// Do the arithmetic on unsigned operands to get the same result without |
| 855 | +// signed-overflow UB. |
| 856 | +static long long wrapAdd(long long a, long long b) { |
| 857 | + return static_cast<long long>(static_cast<unsigned long long>(a) + static_cast<unsigned long long>(b)); |
| 858 | +} |
| 859 | + |
| 860 | +static long long wrapSub(long long a, long long b) { |
| 861 | + return static_cast<long long>(static_cast<unsigned long long>(a) - static_cast<unsigned long long>(b)); |
| 862 | +} |
| 863 | + |
| 864 | +static long long wrapMul(long long a, long long b) { |
| 865 | + return static_cast<long long>(static_cast<unsigned long long>(a) * static_cast<unsigned long long>(b)); |
| 866 | +} |
| 867 | + |
851 | 868 | // Evaluate a $([ClassName]::Method(args)) static property function. |
852 | 869 | // Returns an empty string for unknown or unimplementable functions rather |
853 | 870 | // than throwing, so import can continue gracefully. |
@@ -894,23 +911,31 @@ std::string ImportProject::applyMSBuildStaticFunction(const std::string &classNa |
894 | 911 | long long a = 0, b = 0; |
895 | 912 | if (toInt(args[0], a) && toInt(args[1], b)) { |
896 | 913 | if (caseInsensitiveStringCompare(member, "Add") == 0) |
897 | | - return std::to_string(a + b); |
| 914 | + return std::to_string(wrapAdd(a, b)); |
898 | 915 | if (caseInsensitiveStringCompare(member, "Subtract") == 0) |
899 | | - return std::to_string(a - b); |
| 916 | + return std::to_string(wrapSub(a, b)); |
900 | 917 | if (caseInsensitiveStringCompare(member, "Multiply") == 0) |
901 | | - return std::to_string(a * b); |
| 918 | + return std::to_string(wrapMul(a, b)); |
902 | 919 | if (caseInsensitiveStringCompare(member, "Divide") == 0) { |
903 | 920 | if (b == 0) { |
904 | 921 | addDebug("MSBuild::Divide: division by zero"); |
905 | 922 | return std::string(); |
906 | 923 | } |
| 924 | + if (a == LLONG_MIN && b == -1) { |
| 925 | + addDebug("MSBuild::Divide: integer overflow"); |
| 926 | + return std::string(); |
| 927 | + } |
907 | 928 | return std::to_string(a / b); |
908 | 929 | } |
909 | 930 | if (caseInsensitiveStringCompare(member, "Modulo") == 0) { |
910 | 931 | if (b == 0) { |
911 | 932 | addDebug("MSBuild::Modulo: division by zero"); |
912 | 933 | return std::string(); |
913 | 934 | } |
| 935 | + if (a == LLONG_MIN && b == -1) { |
| 936 | + addDebug("MSBuild::Modulo: integer overflow"); |
| 937 | + return std::string(); |
| 938 | + } |
914 | 939 | return std::to_string(a % b); |
915 | 940 | } |
916 | 941 | } |
|
0 commit comments