Commit message: Fixed recursive function calls
CVE: CVE-2019-20016
CWE: CWE-476
Vulnerability description: libmysofa before 2019-11-24 does not properly restrict recursive function calls, as demonstrated by reports of stack consumption in readOHDRHeaderMessageDatatype in dataobject.c and directblockRead in fractalhead.c. NOTE: a download of v0.9 after 2019-12-06 should fully remediate this issue.

Project: libmysofa
File: fractalhead.c

static int directblockRead(struct READER *reader, struct DATAOBJECT *dataobject,
		struct FRACTALHEAP *fractalheap) {

	char buf[4], *name, *value;
	int size, offset_size, length_size, err, len;
	uint8_t typeandversion;
	uint64_t unknown, heap_header_address, block_offset, block_size, offset,
			length;
	long store;
	struct DIR *dir;
	struct MYSOFA_ATTRIBUTE *attr;

	UNUSED(offset);
	UNUSED(block_size);
	UNUSED(block_offset);

	if(reader->recursive_counter >= 10)
		return MYSOFA_INVALID_FORMAT;
	else
		reader->recursive_counter++;

	/* read signature */
	if (fread(buf, 1, 4, reader->fhd) != 4 || strncmp(buf, "FHDB", 4)) {
		log("cannot read signature of fractal heap indirect block\n");
		return MYSOFA_INVALID_FORMAT;
	}
	log("%08" PRIX64 " %.4s stack %d\n", (uint64_t )ftell(reader->fhd) - 4, buf, reader->recursive_counter);

	if (fgetc(reader->fhd) != 0) {
		log("object FHDB must have version 0\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	/* ignore heap_header_address */
	if (fseek(reader->fhd, reader->superblock.size_of_offsets, SEEK_CUR) < 0)
		return errno;

	size = (fractalheap->maximum_heap_size + 7) / 8;
	block_offset = readValue(reader, size);

	if (fractalheap->flags & 2)
		if (fseek(reader->fhd, 4, SEEK_CUR))
			return errno;

	offset_size = ceilf(log2f(fractalheap->maximum_heap_size) / 8);
	if (fractalheap->maximum_direct_block_size < fractalheap->maximum_size)
		length_size = ceilf(log2f(fractalheap->maximum_direct_block_size) / 8);
	else
		length_size = ceilf(log2f(fractalheap->maximum_size) / 8);

	log(" %d %" PRIu64 " %d\n",size,block_offset,offset_size);

	/*
	 * 00003e00  00 46 48 44 42 00 40 02  00 00 00 00 00 00 00 00  |.FHDB.@.........|
	 00003e10  00 00 00 83 8d ac f6 >03  00 0c 00 08 00 04 00 00  |................|
	 00003e20  43 6f 6e 76 65 6e 74 69  6f 6e 73 00 13 00 00 00  |Conventions.....|
	 00003e30  04 00 00 00 02 00 00 00  53 4f 46 41< 03 00 08 00  |........SOFA....|
	 00003e40  08 00 04 00 00 56 65 72  73 69 6f 6e 00 13 00 00  |.....Version....|
	 00003e50  00 03 00 00 00 02 00 00  00 30 2e 36 03 00 10 00  |.........0.6....|
	 00003e60  08 00 04 00 00 53 4f 46  41 43 6f 6e 76 65 6e 74  |.....SOFAConvent|
	 00003e70  69 6f 6e 73 00 13 00 00  00 13 00 00 00 02 00 00  |ions............|
	 00003e80  00 53 69 6d 70 6c 65 46  72 65 65 46 69 65 6c 64  |.SimpleFreeField|
	 00003e90  48 52 49 52 03 00 17 00  08 00 04 00 00 53 4f 46  |HRIR.........SOF|
	 00003ea0  41 43 6f 6e 76 65 6e 74  69 6f 6e 73 56 65 72 73  |AConventionsVers|
	 00003eb0  69 6f 6e 00 13 00 00 00  03 00 00 00 02 00 00 00  |ion.............|
	 *
	 */
	do {
		typeandversion = (uint8_t) fgetc(reader->fhd);
		offset = readValue(reader, offset_size);
		length = readValue(reader, length_size);
		if (offset > 0x10000000 || length > 0x10000000)
			return MYSOFA_UNSUPPORTED_FORMAT;

		log(" %d %4" PRIX64 " %" PRIX64 " %08lX\n",typeandversion,offset,length,ftell(reader->fhd));

		/* TODO: for the following part, the specification is incomplete */
		if (typeandversion == 3) {
			/*
			 * this seems to be a name and value pair
			 */

			if (readValue(reader, 5) != 0x0000040008) {
				log("FHDB type 3 unsupported values");
				return MYSOFA_UNSUPPORTED_FORMAT;
			}

			if (!(name = malloc(length+1)))
				return MYSOFA_NO_MEMORY;
			if (fread(name, 1, length, reader->fhd) != length) {
				free(name);
				return MYSOFA_READ_ERROR;
			}
			name[length]=0;	

			if (readValue(reader, 4) != 0x00000013) {
				log("FHDB type 3 unsupported values");
				free(name);
				return MYSOFA_UNSUPPORTED_FORMAT;
			}

			len = (int) readValue(reader, 2);
			if (len > 0x1000 || len < 0) {
				free(name);
				return MYSOFA_UNSUPPORTED_FORMAT;
			}

			/* TODO: Get definition of this field */
			unknown = readValue(reader, 6);
			if (unknown == 0x000000020200)
				value = NULL;
			else if (unknown == 0x000000020000) {
				if (!(value = malloc(len + 1))) {
					free(name);
					return MYSOFA_NO_MEMORY;
				}
				if (fread(value, 1, len, reader->fhd) != len) {
					free(value);
					free(name);
					return MYSOFA_READ_ERROR;
				}
				value[len] = 0;
			} else if (unknown == 0x20000020000) {
				if (!(value = malloc(5))) {
					free(name);
					return MYSOFA_NO_MEMORY;
				}
				strcpy(value, "");
			} else {
				log("FHDB type 3 unsupported values: %12" PRIX64 "\n",unknown);
				free(name);
				/* TODO:			return MYSOFA_UNSUPPORTED_FORMAT; */
				return MYSOFA_OK;
			} 
			log(" %s = %s\n", name, value);

			attr = malloc(sizeof(struct MYSOFA_ATTRIBUTE));
			attr->name = name;
			attr->value = value;
			attr->next = dataobject->attributes;
			dataobject->attributes = attr;

		} else if (typeandversion == 1) {
			/*
			 * pointer to another data object
			 */
			unknown = readValue(reader, 6);
			if (unknown) {
				log("FHDB type 1 unsupported values\n");
				return MYSOFA_UNSUPPORTED_FORMAT;
			}

			len = fgetc(reader->fhd);
			if (len < 0)
				return MYSOFA_READ_ERROR;
			assert(len < 0x100);

			if (!(name = malloc(len + 1)))
				return MYSOFA_NO_MEMORY;
			if (fread(name, 1, len, reader->fhd) != len) {
				free(name);
				return MYSOFA_READ_ERROR;
			}
			name[len] = 0;

			heap_header_address = readValue(reader,
					reader->superblock.size_of_offsets);

			log("\nfractal head type 1 length %4" PRIX64 " name %s address %" PRIX64 "\n", length, name, heap_header_address);

			dir = malloc(sizeof(struct DIR));
			if (!dir) {
				free(name);
				return MYSOFA_NO_MEMORY;
			}
			memset(dir, 0, sizeof(*dir));

			dir->next = dataobject->directory;
			dataobject->directory = dir;

			store = ftell(reader->fhd);
			if (fseek(reader->fhd, heap_header_address, SEEK_SET)) {
				free(name);
				return errno;
			}

			err = dataobjectRead(reader, &dir->dataobject, name);
			if (err) {
				return err;
			}

			if (store < 0) {
				return errno;
			}
			if (fseek(reader->fhd, store, SEEK_SET) < 0)
				return errno;

		} else if (typeandversion != 0) {
			/* TODO is must be avoided somehow */
			log("fractal head unknown type %d\n", typeandversion);
			/*			return MYSOFA_UNSUPPORTED_FORMAT; */
			return MYSOFA_OK;
		}

	} while (typeandversion != 0);

	reader->recursive_counter--;
	return MYSOFA_OK;
}

Project: libmysofa
File: reader.c

MYSOFA_EXPORT struct MYSOFA_HRTF* mysofa_load(const char *filename, int *err) {
	struct READER reader;
	struct MYSOFA_HRTF *hrtf = NULL;

	if (filename == NULL)
		filename = CMAKE_INSTALL_PREFIX "/share/libmysofa/default.sofa";

	if (strcmp(filename, "-"))
		reader.fhd = fopen(filename, "rb");
	else
		reader.fhd = stdin;

	if (!reader.fhd) {
		log("cannot open file %s\n", filename);
		*err = errno;
		return NULL;
	}
	reader.gcol = NULL;
	reader.all = NULL;
	reader.recursive_counter = 0;

	*err = superblockRead(&reader, &reader.superblock);

	if (!*err) {
		hrtf = getHrtf(&reader, err);
	}

	superblockFree(&reader, &reader.superblock);
	gcolFree(reader.gcol);
	if (strcmp(filename, "-"))
		fclose(reader.fhd);

	return hrtf;
}

Project: libmysofa
File: reader.c

MYSOFA_EXPORT void mysofa_getversion(int *major, int *minor, int *patch) {
	*major = CPACK_PACKAGE_VERSION_MAJOR;
	*minor = CPACK_PACKAGE_VERSION_MINOR;
	*patch = CPACK_PACKAGE_VERSION_PATCH;
}

Project: libmysofa
File: reader.c

static int mystrcmp(char *s1, char *s2) {
	if (s1 == NULL && s2 == NULL)
		return 0;
	if (s1 == NULL)
		return -1;
	if (s2 == NULL)
		return 1;
	return strcmp(s1, s2);
}

Project: libmysofa
File: reader.c

int validAddress(struct READER *reader, uint64_t address) {
	return address > 0 && address < reader->superblock.end_of_file_address;
}

Project: libmysofa
File: reader.c

static void arrayFree(struct MYSOFA_ARRAY *array) {
	while (array->attributes) {
		struct MYSOFA_ATTRIBUTE *next = array->attributes->next;
		free(array->attributes->name);
		free(array->attributes->value);
		free(array->attributes);
		array->attributes = next;
	}
	free(array->values);
}

Project: libmysofa
File: fractalhead.c

void fractalheapFree(struct FRACTALHEAP *fractalheap) {
	free(fractalheap->filter_information);
}

Project: libmysofa
File: reader.c

static struct MYSOFA_HRTF *getHrtf(struct READER *reader, int *err) {
	int dimensionflags = 0;
	struct DIR *dir = reader->superblock.dataobject.directory;

	struct MYSOFA_HRTF *hrtf = malloc(sizeof(struct MYSOFA_HRTF));
	if (!hrtf) {
		*err = errno;
		return NULL;
	}
	memset(hrtf, 0, sizeof(struct MYSOFA_HRTF));

	/* copy SOFA file attributes */
	hrtf->attributes = reader->superblock.dataobject.attributes;
	reader->superblock.dataobject.attributes = NULL;

	/* check SOFA file attributes */
	if (!!(*err = checkAttribute(hrtf->attributes, "Conventions", "SOFA")))
		goto error;

	/* read dimensions */
	while (dir) {
		if (dir->dataobject.name && dir->dataobject.name[0]
				&& dir->dataobject.name[1] == 0) {
			switch (dir->dataobject.name[0]) {
			case 'I':
				*err = getDimension(&hrtf->I, &dir->dataobject);
				dimensionflags |= 1;
				break;
			case 'C':
				*err = getDimension(&hrtf->C, &dir->dataobject);
				dimensionflags |= 2;
				break;
			case 'R':
				*err = getDimension(&hrtf->R, &dir->dataobject);
				dimensionflags |= 4;
				break;
			case 'E':
				*err = getDimension(&hrtf->E, &dir->dataobject);
				dimensionflags |= 8;
				break;
			case 'N':
				*err = getDimension(&hrtf->N, &dir->dataobject);
				dimensionflags |= 0x10;
				break;
			case 'M':
				*err = getDimension(&hrtf->M, &dir->dataobject);
				dimensionflags |= 0x20;
				break;
			case 'S':
				break; /* be graceful, some issues with API version 0.4.4 */
			default:
				log("UNKNOWN SOFA VARIABLE %s", dir->dataobject.name);
				goto error;
			}
			if (*err)
				goto error;
		}
		dir = dir->next;
	}

	if (dimensionflags != 0x3f || hrtf->I != 1 || hrtf->C != 3) {
		log("dimensions are missing or wrong\n");
		goto error;
	}

	dir = reader->superblock.dataobject.directory;
	while (dir) {

		if(!dir->dataobject.name) {
			log("SOFA VARIABLE IS NULL.\n");
		}
		else if (!strcmp(dir->dataobject.name, "ListenerPosition")) {
			*err = getArray(&hrtf->ListenerPosition, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "ReceiverPosition")) {
			*err = getArray(&hrtf->ReceiverPosition, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "SourcePosition")) {
			*err = getArray(&hrtf->SourcePosition, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "EmitterPosition")) {
			*err = getArray(&hrtf->EmitterPosition, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "ListenerUp")) {
			*err = getArray(&hrtf->ListenerUp, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "ListenerView")) {
			*err = getArray(&hrtf->ListenerView, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "Data.IR")) {
			*err = getArray(&hrtf->DataIR, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "Data.SamplingRate")) {
			*err = getArray(&hrtf->DataSamplingRate, &dir->dataobject);
		} else if (!strcmp(dir->dataobject.name, "Data.Delay")) {
			*err = getArray(&hrtf->DataDelay, &dir->dataobject);
		} else {
			if (!(dir->dataobject.name[0] && !dir->dataobject.name[1]))
				log("UNKNOWN SOFA VARIABLE %s.\n", dir->dataobject.name);
		}
		dir = dir->next;
	}

	return hrtf;

	error: free(hrtf);
	if (!*err)
		*err = MYSOFA_INVALID_FORMAT;
	return NULL;
}

Project: libmysofa
File: reader.c

static int getArray(struct MYSOFA_ARRAY *array, struct DATAOBJECT *dataobject) {
	float *p1;
	double *p2;
	int i;

	struct MYSOFA_ATTRIBUTE *attr = dataobject->attributes;
	while (attr) {
		log(" %s=%s\n",attr->name,attr->value);

		attr = attr->next;
	}

	if (dataobject->dt.u.f.bit_precision != 64)
		return MYSOFA_UNSUPPORTED_FORMAT;

	array->attributes = dataobject->attributes;
	dataobject->attributes = NULL;
	array->elements = dataobject->data_len / 8;

	p1 = dataobject->data;
	p2 = dataobject->data;
	for (i = 0; i < array->elements; i++)
		*p1++ = *p2++;
	array->values = realloc(dataobject->data, array->elements * sizeof(float));

	dataobject->data = NULL;

	return MYSOFA_OK;
}

Project: libmysofa
File: reader.c

uint64_t readValue(struct READER *reader, int size) {
	int i, c;
	uint64_t value;
	c = fgetc(reader->fhd);
	if (c < 0)
		return 0xffffffffffffffffLL;
	value = (uint8_t) c;
	for (i = 1; i < size; i++) {
		c = fgetc(reader->fhd);
		if (c < 0)
			return 0xffffffffffffffffLL;
		value |= ((uint64_t) c) << (i * 8);
	}
	return value;
}

Project: libmysofa
File: reader.c

static int checkAttribute(struct MYSOFA_ATTRIBUTE *attribute, char *name,
		char *value) {
	while (attribute) {
		if (!mystrcmp(attribute->name, name)
				&& !mystrcmp(attribute->value, value))
			return MYSOFA_OK;
		attribute = attribute->next;
	}

	return MYSOFA_INVALID_FORMAT;
}

Project: libmysofa
File: reader.c

static int getDimension(unsigned *dim, struct DATAOBJECT *dataobject) {
	int err;
	struct MYSOFA_ATTRIBUTE *attr = dataobject->attributes;

	if (!!(err = checkAttribute(dataobject->attributes, "CLASS",
			"DIMENSION_SCALE")))
		return err;

	while (attr) {
		log(" %s=%s\n",attr->name,attr->value);

		if (!strcmp(attr->name, "NAME")
				&& attr->value && !strncmp(attr->value,
						"This is a netCDF dimension but not a netCDF variable.",
						53)) {
			char *p = attr->value + strlen(attr->value) - 1;
			while (isdigit(*p)) {
				p--;
			}
			p++;
			*dim = atoi(p);
			log("NETCDF DIM %u\n",*dim);
			return MYSOFA_OK;
		}
		attr = attr->next;
	}
	return MYSOFA_INVALID_FORMAT;
}

Project: libmysofa
File: fractalhead.c

static int log2i(int a) {
	return round(log2(a));
}

Project: libmysofa
File: reader.c

MYSOFA_EXPORT void mysofa_free(struct MYSOFA_HRTF *hrtf) {
	if (!hrtf)
		return;

	while (hrtf->attributes) {
		struct MYSOFA_ATTRIBUTE *next = hrtf->attributes->next;
		free(hrtf->attributes->name);
		free(hrtf->attributes->value);
		free(hrtf->attributes);
		hrtf->attributes = next;
	}

	arrayFree(&hrtf->ListenerPosition);
	arrayFree(&hrtf->ReceiverPosition);
	arrayFree(&hrtf->SourcePosition);
	arrayFree(&hrtf->EmitterPosition);
	arrayFree(&hrtf->ListenerUp);
	arrayFree(&hrtf->ListenerView);
	arrayFree(&hrtf->DataIR);
	arrayFree(&hrtf->DataSamplingRate);
	arrayFree(&hrtf->DataDelay);
	free(hrtf);
}

Project: libmysofa
File: fractalhead.c

static int indirectblockRead(struct READER *reader,
		struct DATAOBJECT *dataobject, struct FRACTALHEAP *fractalheap,
		uint64_t iblock_size) {
	int size, nrows, max_dblock_rows, k, n, err;
	uint32_t filter_mask;
	uint64_t heap_header_address, block_offset, child_direct_block = 0,
			size_filtered, child_indirect_block;
	long store;

	char buf[4];

	UNUSED(size_filtered);
	UNUSED(heap_header_address);
	UNUSED(filter_mask);

	/* read signature */
	if (fread(buf, 1, 4, reader->fhd) != 4 || strncmp(buf, "FHIB", 4)) {
		log("cannot read signature of fractal heap indirect block\n");
		return MYSOFA_INVALID_FORMAT;
	}
	log("%08" PRIX64 " %.4s\n", (uint64_t )ftell(reader->fhd) - 4, buf);

	if (fgetc(reader->fhd) != 0) {
		log("object FHIB must have version 0\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	/* ignore it */
	heap_header_address = readValue(reader, reader->superblock.size_of_offsets);

	size = (fractalheap->maximum_heap_size + 7) / 8;
	block_offset = readValue(reader, size);

	if (block_offset) {
		log("FHIB block offset is not 0\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	/*	 The number of rows of blocks, nrows, in an indirect block of size iblock_size is given by the following expression: */
	nrows = (log2i(iblock_size) - log2i(fractalheap->starting_block_size)) + 1;

	/* The maximum number of rows of direct blocks, max_dblock_rows, in any indirect block of a fractal heap is given by the following expression: */
	max_dblock_rows = (log2i(fractalheap->maximum_direct_block_size)
			- log2i(fractalheap->starting_block_size)) + 2;

	/* Using the computed values for nrows and max_dblock_rows, along with the Width of the doubling table, the number of direct and indirect block entries (K and N in the indirect block description, below) in an indirect block can be computed: */
	if (nrows < max_dblock_rows)
		k = nrows * fractalheap->table_width;
	else
		k = max_dblock_rows * fractalheap->table_width;

	/* If nrows is less than or equal to max_dblock_rows, N is 0. Otherwise, N is simply computed: */
	n = k - (max_dblock_rows * fractalheap->table_width);

	while (k > 0) {
		child_direct_block = readValue(reader,
				reader->superblock.size_of_offsets);
		if (fractalheap->encoded_length > 0) {
			size_filtered = readValue(reader,
					reader->superblock.size_of_lengths);
			filter_mask = readValue(reader, 4);
		}
		log(">> %d %" PRIX64 " %d\n",k,child_direct_block,size);
		if (validAddress(reader, child_direct_block)) {
			store = ftell(reader->fhd);
			if (fseek(reader->fhd, child_direct_block, SEEK_SET) < 0)
				return errno;
			err = directblockRead(reader, dataobject, fractalheap);
			if (err)
				return err;
			if (store < 0)
				return MYSOFA_READ_ERROR;
			if (fseek(reader->fhd, store, SEEK_SET) < 0)
				return errno;
		}

		k--;
	}

	while (n > 0) {
		child_indirect_block = readValue(reader,
				reader->superblock.size_of_offsets);

		if (validAddress(reader, child_direct_block)) {
			store = ftell(reader->fhd);
			if (fseek(reader->fhd, child_indirect_block, SEEK_SET) < 0)
				return errno;
			err = indirectblockRead(reader, dataobject, fractalheap,
					iblock_size * 2);
			if (err)
				return err;
			if (store < 0)
				return MYSOFA_READ_ERROR;
			if (fseek(reader->fhd, store, SEEK_SET) < 0)
				return errno;
		}

		n--;
	}

	return MYSOFA_OK;
}

Project: libmysofa
File: fractalhead.c

int fractalheapRead(struct READER *reader, struct DATAOBJECT *dataobject,
		struct FRACTALHEAP *fractalheap) {
	int err;
	char buf[4];

	/* read signature */
	if (fread(buf, 1, 4, reader->fhd) != 4 || strncmp(buf, "FRHP", 4)) {
		log("cannot read signature of fractal heap\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}
	log("%" PRIX64 " %.4s\n", (uint64_t )ftell(reader->fhd) - 4, buf);

	if (fgetc(reader->fhd) != 0) {
		log("object fractal heap must have version 0\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	fractalheap->heap_id_length = (uint16_t) readValue(reader, 2);
	fractalheap->encoded_length = (uint16_t) readValue(reader, 2);
	if (fractalheap->encoded_length > 0x8000)
		return MYSOFA_UNSUPPORTED_FORMAT;
	fractalheap->flags = (uint8_t) fgetc(reader->fhd);
	fractalheap->maximum_size = (uint32_t) readValue(reader, 4);

	fractalheap->next_huge_object_id = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->btree_address_of_huge_objects = readValue(reader,
			reader->superblock.size_of_offsets);
	fractalheap->free_space = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->address_free_space = readValue(reader,
			reader->superblock.size_of_offsets);
	fractalheap->amount_managed_space = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->amount_allocated_space = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->offset_managed_space = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->number_managed_objects = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->size_huge_objects = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->number_huge_objects = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->size_tiny_objects = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->number_tiny_objects = readValue(reader,
			reader->superblock.size_of_lengths);

	fractalheap->table_width = (uint16_t) readValue(reader, 2);

	fractalheap->starting_block_size = readValue(reader,
			reader->superblock.size_of_lengths);
	fractalheap->maximum_direct_block_size = readValue(reader,
			reader->superblock.size_of_lengths);

	fractalheap->maximum_heap_size = (uint16_t) readValue(reader, 2);
	fractalheap->starting_row = (uint16_t) readValue(reader, 2);

	fractalheap->address_of_root_block = readValue(reader,
			reader->superblock.size_of_offsets);

	fractalheap->current_row = (uint16_t) readValue(reader, 2);

	if (fractalheap->encoded_length > 0) {

		fractalheap->size_of_filtered_block = readValue(reader,
				reader->superblock.size_of_lengths);
		fractalheap->fitler_mask = (uint32_t) readValue(reader, 4);

		fractalheap->filter_information = malloc(fractalheap->encoded_length);
		if (!fractalheap->filter_information)
			return MYSOFA_NO_MEMORY;

		if (fread(fractalheap->filter_information, 1,
				fractalheap->encoded_length, reader->fhd)
				!= fractalheap->encoded_length) {
			free(fractalheap->filter_information);
			return MYSOFA_READ_ERROR;
		}
	}

	if (fseek(reader->fhd, 4, SEEK_CUR) < 0) { /* skip checksum */
		return MYSOFA_READ_ERROR;
	}

	if (fractalheap->number_huge_objects) {
		log("cannot handle huge objects\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	if (fractalheap->number_tiny_objects) {
		log("cannot handle tiny objects\n");
		return MYSOFA_UNSUPPORTED_FORMAT;
	}

	if (validAddress(reader, fractalheap->address_of_root_block)) {

		if (fseek(reader->fhd, fractalheap->address_of_root_block, SEEK_SET)
				< 0)
			return errno;
		if (fractalheap->current_row)
			err = indirectblockRead(reader, dataobject, fractalheap,
					fractalheap->starting_block_size);
		else {
			err = directblockRead(reader, dataobject, fractalheap);

		}
		if (err)
			return err;
	}

	return MYSOFA_OK;
}