From fef5ad8a42c040b001eae95ff2cebfce968959e9 Mon Sep 17 00:00:00 2001
From: Ross Lagerwall <ross.lagerwall@citrix.com>
Date: Fri, 21 Feb 2025 12:15:35 +0000
Subject: [PATCH 1/2] Fix buffer overflow parsing VPD page 0x83
The VPD page 0x83 may contain unexpected information, causing smartd to
want to write more than the allocated buffer. The existing use of
snprintf in scsi_decode_lu_dev_id() is incorrect. If the buffer is too
small, snprintf returns the number of bytes it would have written
which gets added to the total (si). On the next call to snprintf, it
subtracts the total written from the size of the buffer and gets a
negative integer which is passed as a size_t, becoming a very large
positive integer and leaving snprintf to freely overflow the buffer.
Defend against this by ensuring the size passed to snprintf is >= 0.
Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
scsicmds.cpp | 38 ++++++++++++++++++++------------------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/scsicmds.cpp b/scsicmds.cpp
index 2d1190659c15..e5e26851685a 100644
a
|
b
|
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
755 | 755 | return -1; |
756 | 756 | } |
757 | 757 | |
| 758 | #define SLEN(a, b) ((a) > (b) ? ((a) - (b)) : 0) |
758 | 759 | s[0] = '\0'; |
759 | 760 | int si = 0; |
760 | 761 | int have_scsi_ns = 0; |
… |
… |
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
764 | 765 | const unsigned char * ucp = b + off; |
765 | 766 | int i_len = ucp[3]; |
766 | 767 | if ((off + i_len + 4) > blen) { |
767 | | snprintf(s+si, slen-si, "error: designator length"); |
| 768 | snprintf(s+si, SLEN(slen, si), "error: designator length"); |
768 | 769 | return -1; |
769 | 770 | } |
770 | 771 | int assoc = ((ucp[1] >> 4) & 0x3); |
… |
… |
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
783 | 784 | break; |
784 | 785 | case 2: /* EUI-64 based */ |
785 | 786 | if ((8 != i_len) && (12 != i_len) && (16 != i_len)) { |
786 | | snprintf(s+si, slen-si, "error: EUI-64 length"); |
| 787 | snprintf(s+si, SLEN(slen, si), "error: EUI-64 length"); |
787 | 788 | return -1; |
788 | 789 | } |
789 | 790 | if (have_scsi_ns) |
790 | 791 | si = 0; |
791 | | si += snprintf(s+si, slen-si, "0x"); |
| 792 | si += snprintf(s+si, SLEN(slen, si), "0x"); |
792 | 793 | for (int m = 0; m < i_len; ++m) |
793 | | si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); |
| 794 | si += snprintf(s+si, SLEN(slen, si), "%02x", (unsigned int)ip[m]); |
794 | 795 | break; |
795 | 796 | case 3: /* NAA */ |
796 | 797 | if (1 != c_set) { |
797 | | snprintf(s+si, slen-si, "error: NAA bad code_set"); |
| 798 | snprintf(s+si, SLEN(slen, si), "error: NAA bad code_set"); |
798 | 799 | return -1; |
799 | 800 | } |
800 | 801 | naa = (ip[0] >> 4) & 0xff; |
801 | 802 | if ((naa < 2) || (naa > 6) || (4 == naa)) { |
802 | | snprintf(s+si, slen-si, "error: unexpected NAA"); |
| 803 | snprintf(s+si, SLEN(slen, si), "error: unexpected NAA"); |
803 | 804 | return -1; |
804 | 805 | } |
805 | 806 | if (have_scsi_ns) |
806 | 807 | si = 0; |
807 | 808 | if (2 == naa) { /* NAA IEEE Extended */ |
808 | 809 | if (8 != i_len) { |
809 | | snprintf(s+si, slen-si, "error: NAA 2 length"); |
| 810 | snprintf(s+si, SLEN(slen, si), "error: NAA 2 length"); |
810 | 811 | return -1; |
811 | 812 | } |
812 | | si += snprintf(s+si, slen-si, "0x"); |
| 813 | si += snprintf(s+si, SLEN(slen, si), "0x"); |
813 | 814 | for (int m = 0; m < 8; ++m) |
814 | | si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); |
| 815 | si += snprintf(s+si, SLEN(slen, si), "%02x", (unsigned int)ip[m]); |
815 | 816 | } else if ((3 == naa ) || (5 == naa)) { |
816 | 817 | /* NAA=3 Locally assigned; NAA=5 IEEE Registered */ |
817 | 818 | if (8 != i_len) { |
818 | | snprintf(s+si, slen-si, "error: NAA 3 or 5 length"); |
| 819 | snprintf(s+si, SLEN(slen, si), "error: NAA 3 or 5 length"); |
819 | 820 | return -1; |
820 | 821 | } |
821 | | si += snprintf(s+si, slen-si, "0x"); |
| 822 | si += snprintf(s+si, SLEN(slen, si), "0x"); |
822 | 823 | for (int m = 0; m < 8; ++m) |
823 | | si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); |
| 824 | si += snprintf(s+si, SLEN(slen, si), "%02x", (unsigned int)ip[m]); |
824 | 825 | } else if (6 == naa) { /* NAA IEEE Registered extended */ |
825 | 826 | if (16 != i_len) { |
826 | | snprintf(s+si, slen-si, "error: NAA 6 length"); |
| 827 | snprintf(s+si, SLEN(slen, si), "error: NAA 6 length"); |
827 | 828 | return -1; |
828 | 829 | } |
829 | | si += snprintf(s+si, slen-si, "0x"); |
| 830 | si += snprintf(s+si, SLEN(slen, si), "0x"); |
830 | 831 | for (int m = 0; m < 16; ++m) |
831 | | si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); |
| 832 | si += snprintf(s+si, SLEN(slen, si), "%02x", (unsigned int)ip[m]); |
832 | 833 | } |
833 | 834 | break; |
834 | 835 | case 4: /* Relative target port */ |
… |
… |
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
838 | 839 | break; |
839 | 840 | case 8: /* SCSI name string */ |
840 | 841 | if (3 != c_set) { |
841 | | snprintf(s+si, slen-si, "error: SCSI name string"); |
| 842 | snprintf(s+si, SLEN(slen, si), "error: SCSI name string"); |
842 | 843 | return -1; |
843 | 844 | } |
844 | 845 | /* does %s print out UTF-8 ok?? */ |
845 | 846 | if (si == 0) { |
846 | | si += snprintf(s+si, slen-si, "%s", (const char *)ip); |
| 847 | si += snprintf(s+si, SLEN(slen, si), "%s", (const char *)ip); |
847 | 848 | ++have_scsi_ns; |
848 | 849 | } |
849 | 850 | break; |
… |
… |
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
852 | 853 | } |
853 | 854 | } |
854 | 855 | if (-2 == u) { |
855 | | snprintf(s+si, slen-si, "error: bad structure"); |
| 856 | snprintf(s+si, SLEN(slen, si), "error: bad structure"); |
856 | 857 | return -1; |
857 | 858 | } |
858 | 859 | return 0; |
| 860 | #undef SLEN |
859 | 861 | } |
860 | 862 | |
861 | 863 | /* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if |