Friday, 20 January 2012

Things You Probably Didn't Want to Know About EDID

It's time for another notes.txt, this time from a long-weekend in May 2009 and shared with a few people at the time (about 80 on a mailing list) . If you have the time/inclination to take it any further please do...

So during our travels in May in 2009 we came across Extended Display Identification Data (EDID) [1][2]. What is EDID you might ask? Well EDID is the thing which tells the graphics card or operating system about the monitor you have just plugged in. From the wikipedia article [1]:
"The channel for transmitting the EDID from the display to the graphics card is usually the I2C bus. The combination of EDID and I2C is called the Display Data Channel version 2, or DDC2. The 2 distinguishes it from VESA's original DDC, which used a different serial format."
Now why is this interesting? Well, there are some varible(ish) length strings (see specs bit below):

57: Block type
      FFh Monitor Serial Number
      FEh ASCII string
      FCh Monitor name

Now the specification says (originally at the end of page 13, in a file called EEDIDguideV1.pdf which VESA have removed/hidden from their site since 2009 - although you can find copies down the back of the Internet's sofa, if you break out some Google foo.):
"Descriptors identified by tag numbers FFh, FEh and FCh contain ASCII strings. The length of the string in the descriptors is limited to 13 characters. Strings that are less than 13 characters must use the line feed character (0Ah) following the final string character and the space character (20h) for all remaining bytes of the descriptor. Use of 00h or 01h to fill remaining bytes is not compliant."
Now the fact it is hard limited means it is unlikely there will be a memory corruption vulnerability (unless the format string gods are kind to us). But lets look at a Linux implementation [4] anyway (the bit that copies the string):

static void copy_string(unsigned char *c, unsigned char *s)
{
  int i;
  c = c + 5;
  for (i = 0; (i < 13 && *c != 0x0A); i++)
    *(s++) = *(c++);
  *s = 0;
  while (i-- && (*--s == 0x20)) *s = 0;
}

Now if we look at the destination for this [5]:
struct fb_monspecs {         struct fb_chroma chroma;         struct fb_videomode *modedb;    /* mode database */         __u8  manufacturer[4];          /* Manufacturer */         __u8  monitor[14];              /* Monitor String */
We see this this is well and truly safe. The only other implementation [6] we found in our five minutes of searching back in 2009 didn't guarantee NULL termination, but no great shakes. It would have had more impact if the destination was a structure, as in the first, as we may have been able to effectively build one big string across the three structure fields (subject to the compiler structure padding of course).

So in our haste in reading the first set of specifications, we missed the extension flag on page 14:
"3.8 Extension Flag and Checksum: 2 bytes offset 7Eh-7Fh"
Extensions, we like extensions; and didn't we find a good one [8] (the other one we found wasn't interesting [9]):
"VESA ENHANCED EDID LOCALIZED STRING EXTENSION STANDARD"
Page 10 [8 - Page 10 - Figure 3.4 String table structure] has the goods:
Manufacturer name string length [1 byte]
Manufacturer name string [x bytes]
Supports UTF8, 16 and 32 encoding, varible length etc. What could possibly go wrong? However sadly we searched high and low and could not find an open source or operating system tool which implemented this feature at the time or even code which parses the extension blocks to perform a sample source code audit upon.

This closes another (and rather dusty) notes.txt, we hope you enjoyed it.

No comments:

Post a Comment