env: fix potential stack overflow in environment functions

Most of the various environment functions create CONFIG_ENV_SIZE buffers on
the stack. At least on ARM and PPC which have 4KB stacks, this can overflow
the stack if we have large environment sizes. So move all the buffers off
the stack to static buffers.

Signed-off-by: Rob Herring <rob.herring@calxeda.com>
diff --git a/common/env_dataflash.c b/common/env_dataflash.c
index 38c9615..0591b99 100644
--- a/common/env_dataflash.c
+++ b/common/env_dataflash.c
@@ -30,6 +30,7 @@
 env_t *env_ptr;
 
 char *env_name_spec = "dataflash";
+static char env_buf[CONFIG_ENV_SIZE];
 
 uchar env_get_char_spec(int index)
 {
@@ -42,11 +43,9 @@
 
 void env_relocate_spec(void)
 {
-	char buf[CONFIG_ENV_SIZE];
+	read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, env_buf);
 
-	read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, buf);
-
-	env_import(buf, 1);
+	env_import(env_buf, 1);
 }
 
 #ifdef CONFIG_ENV_OFFSET_REDUND
@@ -55,20 +54,20 @@
 
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res;
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 
 	return write_dataflash(CONFIG_ENV_ADDR,
-				(unsigned long)&env_new,
+				(unsigned long)env_new,
 				CONFIG_ENV_SIZE);
 }
 
diff --git a/common/env_eeprom.c b/common/env_eeprom.c
index 45c935b..b136f04 100644
--- a/common/env_eeprom.c
+++ b/common/env_eeprom.c
@@ -38,6 +38,7 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 env_t *env_ptr;
+static char env_buf[CONFIG_ENV_SIZE];
 
 char *env_name_spec = "EEPROM";
 int env_eeprom_bus = -1;
@@ -111,7 +112,7 @@
 
 void env_relocate_spec(void)
 {
-	char buf[CONFIG_ENV_SIZE];
+	char *buf = env_buf;
 	unsigned int off = CONFIG_ENV_OFFSET;
 
 #ifdef CONFIG_ENV_OFFSET_REDUND
@@ -126,7 +127,7 @@
 
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res;
 	int	rc;
@@ -138,13 +139,13 @@
 
 	BUG_ON(env_ptr != NULL);
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 
 #ifdef CONFIG_ENV_OFFSET_REDUND
 	if (gd->env_valid == 1) {
@@ -152,11 +153,11 @@
 		off_red	= CONFIG_ENV_OFFSET;
 	}
 
-	env_new.flags = ACTIVE_FLAG;
+	env_new->flags = ACTIVE_FLAG;
 #endif
 
 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
-			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
+			      off, (uchar *)env_new, CONFIG_ENV_SIZE);
 
 #ifdef CONFIG_ENV_OFFSET_REDUND
 	if (rc == 0) {
diff --git a/common/env_fat.c b/common/env_fat.c
index c0f18ab..dd7139d 100644
--- a/common/env_fat.c
+++ b/common/env_fat.c
@@ -37,6 +37,7 @@
 char *env_name_spec = "FAT";
 
 env_t *env_ptr;
+static char env_buf[CONFIG_ENV_SIZE];
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -52,7 +53,7 @@
 #ifdef CONFIG_CMD_SAVEENV
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = env_buf;
 	ssize_t	len;
 	char	*res;
 	block_dev_desc_t *dev_desc = NULL;
@@ -60,7 +61,7 @@
 	int part = FAT_ENV_PART;
 	int err;
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
@@ -95,8 +96,8 @@
 		return 1;
 	}
 
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
-	err = file_fat_write(FAT_ENV_FILE, (void *)&env_new, sizeof(env_t));
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
+	err = file_fat_write(FAT_ENV_FILE, (void *)env_new, sizeof(env_t));
 	if (err == -1) {
 		printf("\n** Unable to write \"%s\" from %s%d:%d **\n",
 			FAT_ENV_FILE, FAT_ENV_INTERFACE, dev, part);
@@ -110,7 +111,7 @@
 
 void env_relocate_spec(void)
 {
-	char buf[CONFIG_ENV_SIZE];
+	char *buf = env_buf;
 	block_dev_desc_t *dev_desc = NULL;
 	int dev = FAT_ENV_DEVICE;
 	int part = FAT_ENV_PART;
diff --git a/common/env_mmc.c b/common/env_mmc.c
index 02bd5ae..f568013 100644
--- a/common/env_mmc.c
+++ b/common/env_mmc.c
@@ -40,6 +40,8 @@
 env_t *env_ptr;
 #endif /* ENV_IS_EMBEDDED */
 
+DEFINE_CACHE_ALIGN_BUFFER(char, env_buf, CONFIG_ENV_SIZE);
+
 DECLARE_GLOBAL_DATA_PTR;
 
 #if !defined(CONFIG_ENV_OFFSET)
@@ -112,7 +114,7 @@
 
 int saveenv(void)
 {
-	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+	env_t *env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res;
 	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
@@ -127,7 +129,7 @@
 		goto fini;
 	}
 
-	res = (char *)&env_new->data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
@@ -135,7 +137,7 @@
 		goto fini;
 	}
 
-	env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 	printf("Writing to MMC(%d)... ", CONFIG_SYS_MMC_ENV_DEV);
 	if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
 		puts("failed\n");
@@ -169,7 +171,6 @@
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
-	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
 	u32 offset;
 	int ret;
@@ -184,12 +185,12 @@
 		goto fini;
 	}
 
-	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
+	if (read_env(mmc, CONFIG_ENV_SIZE, offset, env_buf)) {
 		ret = 1;
 		goto fini;
 	}
 
-	env_import(buf, 1);
+	env_import(env_buf, 1);
 	ret = 0;
 
 fini:
diff --git a/common/env_nand.c b/common/env_nand.c
index 5b69889..8cc2055 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -64,6 +64,8 @@
 env_t *env_ptr;
 #endif /* ENV_IS_EMBEDDED */
 
+DEFINE_CACHE_ALIGN_BUFFER(char, env_buf, CONFIG_ENV_SIZE);
+
 DECLARE_GLOBAL_DATA_PTR;
 
 /*
@@ -173,7 +175,7 @@
 
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res;
 	int	ret = 0;
@@ -185,14 +187,14 @@
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ++env_flags; /* increase the serial */
+	env_new->crc	= crc32(0, env_new->data, ENV_SIZE);
+	env_new->flags	= ++env_flags; /* increase the serial */
 
 	if (gd->env_valid == 1) {
 		puts("Erasing redundant NAND...\n");
@@ -201,7 +203,7 @@
 			return 1;
 
 		puts("Writing to redundant NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
+		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)env_new);
 	} else {
 		puts("Erasing NAND...\n");
 		nand_erase_options.offset = CONFIG_ENV_OFFSET;
@@ -209,7 +211,7 @@
 			return 1;
 
 		puts("Writing to NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
+		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new);
 	}
 	if (ret) {
 		puts("FAILED!\n");
@@ -226,7 +228,7 @@
 int saveenv(void)
 {
 	int	ret = 0;
-	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res;
 	nand_erase_options_t nand_erase_options;
@@ -238,7 +240,7 @@
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
 
-	res = (char *)&env_new->data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
@@ -404,7 +406,6 @@
 {
 #if !defined(ENV_IS_EMBEDDED)
 	int ret;
-	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 
 #if defined(CONFIG_ENV_OFFSET_OOB)
 	ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset);
@@ -420,13 +421,13 @@
 	}
 #endif
 
-	ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
+	ret = readenv(CONFIG_ENV_OFFSET, (u_char *)env_buf);
 	if (ret) {
 		set_default_env("!readenv() failed");
 		return;
 	}
 
-	env_import(buf, 1);
+	env_import(env_buf, 1);
 #endif /* ! ENV_IS_EMBEDDED */
 }
 #endif /* CONFIG_ENV_OFFSET_REDUND */
diff --git a/common/env_nvram.c b/common/env_nvram.c
index eab0e7b..ff74a6c 100644
--- a/common/env_nvram.c
+++ b/common/env_nvram.c
@@ -60,6 +60,10 @@
 char *env_name_spec = "NVRAM";
 
 #ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
+static char env_buf[CONFIG_ENV_SIZE];
+#endif
+
+#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
 uchar env_get_char_spec(int index)
 {
 	uchar c;
@@ -72,36 +76,38 @@
 
 void env_relocate_spec(void)
 {
-	char buf[CONFIG_ENV_SIZE];
+	char *buf;
 
 #if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE)
+	buf = env_buf;
 	nvram_read(buf, CONFIG_ENV_ADDR, CONFIG_ENV_SIZE);
 #else
-	memcpy(buf, (void *)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE);
+	buf = (void *)CONFIG_ENV_ADDR;
 #endif
 	env_import(buf, 1);
 }
 
 int saveenv(void)
 {
-	env_t	env_new;
+#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
+	env_t	*env_new = (env_t *)env_buf;
+#else
+	env_t	*env_new = (env_t *)CONFIG_ENV_ADDR;
+#endif
 	ssize_t	len;
 	char	*res;
 	int	rcode = 0;
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 
 #ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
-	nvram_write(CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE);
-#else
-	if (memcpy((char *)CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE) == NULL)
-		rcode = 1;
+	nvram_write(CONFIG_ENV_ADDR, env_new, CONFIG_ENV_SIZE);
 #endif
 	return rcode;
 }
@@ -115,7 +121,7 @@
 {
 #if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE)
 	ulong crc;
-	uchar data[ENV_SIZE];
+	uchar *data = env_buf;
 
 	nvram_read(&crc, CONFIG_ENV_ADDR, sizeof(ulong));
 	nvram_read(data, CONFIG_ENV_ADDR + sizeof(ulong), ENV_SIZE);
diff --git a/common/env_onenand.c b/common/env_onenand.c
index faa903d..6fd5613 100644
--- a/common/env_onenand.c
+++ b/common/env_onenand.c
@@ -42,6 +42,8 @@
 #define ONENAND_MAX_ENV_SIZE	CONFIG_ENV_SIZE
 #define ONENAND_ENV_SIZE(mtd)	(ONENAND_MAX_ENV_SIZE - ENV_HEADER_SIZE)
 
+static char env_buf[CONFIG_ENV_SIZE];
+
 DECLARE_GLOBAL_DATA_PTR;
 
 void env_relocate_spec(void)
@@ -56,8 +58,7 @@
 	char *buf = (char *)&environment;
 #else
 	loff_t env_addr = CONFIG_ENV_ADDR;
-	char onenand_env[ONENAND_MAX_ENV_SIZE];
-	char *buf = (char *)&onenand_env[0];
+	char *buf = env_buf;
 #endif /* ENV_IS_EMBEDDED */
 
 #ifndef ENV_IS_EMBEDDED
@@ -81,7 +82,7 @@
 
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = env_buf;
 	ssize_t	len;
 	char	*res;
 	struct mtd_info *mtd = &onenand_mtd;
@@ -94,13 +95,13 @@
 		.callback	= NULL,
 	};
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 
 	instr.len = CONFIG_ENV_SIZE;
 #ifdef CONFIG_ENV_ADDR_FLEX
@@ -119,7 +120,7 @@
 	}
 
 	if (mtd->write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
-			(u_char *)&env_new)) {
+			(u_char *)env_new)) {
 		printf("OneNAND: write failed at 0x%llx\n", instr.addr);
 		return 2;
 	}
diff --git a/common/env_sf.c b/common/env_sf.c
index d9e9085..9a592ba 100644
--- a/common/env_sf.c
+++ b/common/env_sf.c
@@ -58,11 +58,12 @@
 char *env_name_spec = "SPI Flash";
 
 static struct spi_flash *env_flash;
+static char env_buf[CONFIG_ENV_SIZE];
 
 #if defined(CONFIG_ENV_OFFSET_REDUND)
 int saveenv(void)
 {
-	env_t	env_new;
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 	char	*res, *saved_buffer = NULL, flag = OBSOLETE_FLAG;
 	u32	saved_size, saved_offset, sector = 1;
@@ -78,14 +79,14 @@
 		}
 	}
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ACTIVE_FLAG;
+	env_new->crc	= crc32(0, env_new->data, ENV_SIZE);
+	env_new->flags	= ACTIVE_FLAG;
 
 	if (gd->env_valid == 1) {
 		env_new_offset = CONFIG_ENV_OFFSET_REDUND;
@@ -125,7 +126,7 @@
 	puts("Writing to SPI flash...");
 
 	ret = spi_flash_write(env_flash, env_new_offset,
-		CONFIG_ENV_SIZE, &env_new);
+		CONFIG_ENV_SIZE, env_new);
 	if (ret)
 		goto done;
 
@@ -137,7 +138,7 @@
 	}
 
 	ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
-				sizeof(env_new.flags), &flag);
+				sizeof(env_new->flags), &flag);
 	if (ret)
 		goto done;
 
@@ -243,7 +244,7 @@
 	u32	saved_size, saved_offset, sector = 1;
 	char	*res, *saved_buffer = NULL;
 	int	ret = 1;
-	env_t	env_new;
+	env_t	*env_new = (env_t *)env_buf;
 	ssize_t	len;
 
 	if (!env_flash) {
@@ -276,13 +277,13 @@
 			sector++;
 	}
 
-	res = (char *)&env_new.data;
+	res = (char *)env_new->data;
 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
 	if (len < 0) {
 		error("Cannot export environment: errno = %d\n", errno);
 		goto done;
 	}
-	env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
 
 	puts("Erasing SPI flash...");
 	ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
@@ -292,7 +293,7 @@
 
 	puts("Writing to SPI flash...");
 	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
-		CONFIG_ENV_SIZE, &env_new);
+		CONFIG_ENV_SIZE, env_new);
 	if (ret)
 		goto done;
 
@@ -315,7 +316,7 @@
 
 void env_relocate_spec(void)
 {
-	char buf[CONFIG_ENV_SIZE];
+	char *buf = env_buf;
 	int ret;
 
 	env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,