Skip to content

Instantly share code, notes, and snippets.

@thentenaar
Created July 24, 2015 18:30
Show Gist options
  • Save thentenaar/649347f4fd07b1d52183 to your computer and use it in GitHub Desktop.
Save thentenaar/649347f4fd07b1d52183 to your computer and use it in GitHub Desktop.
Patch for the paging bug on Solaris 2.5.1 (SunOS 5.5.1) i386
/**
* Patch to fix the paging bug in the SunOS 5.5.1 (i386) kernel
* Copyright (C) 2015 Tim Hentenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PATCH_OFFSET 0x664
#define PATCH_SIZE 42
#define KERNEL_PATH "/platform/i86pc/kernel/unix"
/**
* The original call to enable_4mb_page_support (0xe0400002)
* within pagetable_madevalid (0xe0400555).
*/
static const unsigned char original_bytes[PATCH_SIZE] = {
0xb8, 0x02, 0x00, 0x40, 0xe0, /* mov $0xe0400002, %eax */
0x25, 0xff, 0x0f, 0x00, 0x00, /* and $0xfff, %eax */
0x81, 0xe2, 0x00, 0xf0, 0xff, 0xff, /* and $0xfffff000, %edx */
0x03, 0xc2, /* add %edx, %eax */
0x8b, 0x1d, 0x8b, 0x01, 0x50, 0xe0, /* mov cr4_value, %ebx */
0xff, 0xd0, /* call *%eax */
0x58, /* pop %eax */
0x59, /* pop %ecx */
0x89, 0x08, /* mov %ecx, (%eax) */
0x58, /* pop %eax */
0x59, /* pop %ecx */
0x89, 0x08, /* mov %ecx, (%eax) */
0x0f, 0x20, 0xd8, /* mov %cr3, %eax */
0x0f, 0x22, 0xd8, /* mov %eax, %cr3 */
0xeb, 0x31, /* jmp cpu_done */
};
/**
* Flush the TLB by reading and writing CR3 before indirectly calling
* enable_4mb_page_support to avoid the really nasty page fault on
* 2nd gen. Intel Core processors. I haven't tested this on my
* i7-4810MQ box, but it works like a charm on my i7-2670QM.
*
* Thankfully, enable_4mb_page_support is at .text + 2 bytes.
*
* AFAICT, this shouldn't do any harm elsewhere.
*/
static const unsigned char patch_bytes[PATCH_SIZE] = {
0x0f, 0x20, 0xd8, /* mov %cr3, %eax */
0x0f, 0x22, 0xd8, /* mov %eax, %cr3 */
0x8b, 0xc2, /* mov %edx, %eax */
0x25, 0x00, 0xf0, 0xff, 0xff, /* and %eax, 0xfffff000 */
0x40, /* inc %eax */
0x40, /* inc %eax */
0x8b, 0x1d, 0x8b, 0x01, 0x50, 0xe0, /* mov cr4_value, %ebx */
0xff, 0xd0, /* call *%eax */
0x58, /* pop %eax */
0x59, /* pop %ecx */
0x89, 0x08, /* mov %ecx, (%eax) */
0x58, /* pop %eax */
0x59, /* pop %ecx */
0x89, 0x08, /* mov %ecx, (%eax) */
0x0f, 0x20, 0xd8, /* mov %cr3, %eax */
0x0f, 0x22, 0xd8, /* mov %eax, %cr3 */
0xeb, 0x34, /* jmp cpu_done */
0x90, /* nop */
0x90, /* nop */
0x90, /* nop */
};
/* Buffer for reading from the kernel file */
static char buf[PATCH_SIZE];
/**
* Verify bytes from the kernel file.
*
* \param fp FILE pointer representing the kernel file.
* \param bytes Bytes which should appear at the PATCH_OFFSET.
* \return 0 on success, 1 on error.
*/
static int check_patch_bytes(FILE *fp, const unsigned char *bytes)
{
size_t b = 0;
if (fseek(fp, PATCH_OFFSET, SEEK_SET) == -1) {
perror("Unable to seek: ");
goto err;
}
/* Read the bytes we're about to patch. */
do {
b += fread(buf, 1, PATCH_SIZE - b, fp);
if (feof(fp)) {
puts("Got EOF while reading");
goto err;
}
} while (b < PATCH_SIZE);
/* Ensure we've got the right bytes */
if (memcmp(buf, bytes, sizeof(buf))) {
puts("error: Unexpected bytes at patch offset");
goto err;
}
return 0;
err:
return 1;
}
int main(int argc, char *argv[])
{
FILE *fp;
size_t bytes = 0, written = 0;
fp = fopen(KERNEL_PATH, "r+");
if (!fp) {
perror("Unable to open the kernel: ");
goto err;
}
puts("Verifying kernel file...");
if (check_patch_bytes(fp, original_bytes))
goto err;
if (fseek(fp, PATCH_OFFSET, SEEK_SET) == -1) {
perror("Unable to seek before patching: ");
goto err;
}
puts("Applying patch...");
do {
written = fwrite(patch_bytes, 1, PATCH_SIZE - bytes, fp);
if (!written) break;
else bytes += written;
} while (bytes < PATCH_SIZE);
if (!bytes) {
puts("An error occured while writing the patch");
goto err;
}
puts("Verifying patch...");
if (check_patch_bytes(fp, patch_bytes))
goto err;
puts("Kernel successfully patched");
fclose(fp);
return EXIT_SUCCESS;
err:
fclose(fp);
return EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment