[libvirt PATCH 1/4] PCI VPD: handle additional edge cases

Daniel P. Berrangé berrange at redhat.com
Tue Oct 26 16:37:15 UTC 2021


On Fri, Oct 22, 2021 at 03:45:42PM +0300, Dmitrii Shcherbakov wrote:
> * RV and RW fields must be at the last position in their respective
>   section (per the conditions in the spec). Therefore, the parser now
>   stops iterating over fields as soon as it encounters one of those
>   fields and checks whether the end of the resource has been reached;
> * Individual fields must have a valid length - the parser needs to check
>   for invalid length values that violate boundary conditions of the
>   resource.
> * A zero-length field may be the last one in the resource, however, the
>   boundary check is currently too strict to allow that.
> 
> Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherbakov at canonical.com>
> ---
>  src/util/virpcivpd.c  | 28 +++++++++++----
>  tests/virpcivpdtest.c | 84 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 106 insertions(+), 6 deletions(-)
> 
> diff --git a/src/util/virpcivpd.c b/src/util/virpcivpd.c
> index 8856bca459..cd49031fa4 100644
> --- a/src/util/virpcivpd.c
> +++ b/src/util/virpcivpd.c
> @@ -466,8 +466,12 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, uint16_t resPos, uint16_t re
>  
>      bool hasChecksum = false;
>      bool hasRW = false;
> +    bool endReached = false;
>  
> -    while (fieldPos + 3 < resPos + resDataLen) {
> +    /* Note the equal sign - fields may have a zero length in which case they will
> +     * just occupy 3 header bytes. In the in case of the RW field this may mean that
> +     * no more space is left in the section. */
> +    while (fieldPos + 3 <= resPos + resDataLen) {
>          /* Keyword resources consist of keywords (2 ASCII bytes per the spec) and 1-byte length. */
>          if (virPCIVPDReadVPDBytes(vpdFileFd, buf, 3, fieldPos, csum) != 3) {
>              /* Invalid field encountered which means the resource itself is invalid too. Report
> @@ -518,6 +522,13 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, uint16_t resPos, uint16_t re
>                  return false;
>          }
>  
> +        if (resPos + resDataLen < fieldPos + fieldDataLen) {
> +            /* In this case the field cannot simply be skipped since the position of the
> +             * next field is determined based on the length of a previous field. */
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("A field data length violates the resource length boundary."));
> +            return false;
> +        }
>          if (virPCIVPDReadVPDBytes(vpdFileFd, buf, bytesToRead, fieldPos, csum) != bytesToRead) {
>              virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                             _("Could not parse a resource field data - VPD has invalid format"));
> @@ -546,12 +557,13 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, uint16_t resPos, uint16_t re
>              hasChecksum = true;
>              g_free(g_steal_pointer(&fieldKeyword));
>              g_free(g_steal_pointer(&fieldValue));
> -            continue;
> +            break;
>          } else if (fieldFormat == VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RDWR) {
>              /* Skip the read-write space since it is used for indication only. */
>              hasRW = true;
>              g_free(g_steal_pointer(&fieldKeyword));
>              g_free(g_steal_pointer(&fieldValue));
> +            break;
>          } else if (fieldFormat == VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST) {
>              /* Skip unknown fields */
>              g_free(g_steal_pointer(&fieldKeyword));
> @@ -579,13 +591,17 @@ virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, uint16_t resPos, uint16_t re
>          g_free(g_steal_pointer(&fieldKeyword));
>          g_free(g_steal_pointer(&fieldValue));
>      }
> -    if (readOnly && !hasChecksum) {
> +
> +    /* May have exited the loop prematurely in case RV or RW were encountered and
> +     * they were not the last fields in the section. */
> +    endReached = (fieldPos >= resPos + resDataLen);
> +    if (readOnly && !(hasChecksum && endReached)) {
>          virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> -                       _("VPD-R does not contain the mandatory RV field"));
> +                _("VPD-R does not contain the mandatory RV field as the last field"));
>          return false;
> -    } else if (!readOnly && !hasRW) {
> +    } else if (!readOnly && !(hasRW && endReached)) {
>          virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> -                       _("VPD-W does not contain the mandatory RW field"));
> +                       _("VPD-W does not contain the mandatory RW field as the last field"));
>          return false;

Something is still not right with the logic here as this error report
is triggering on my machines. If I comment out this check, then I can
get data reported by libvirt


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




More information about the libvir-list mailing list