aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Czerner <lczerner@redhat.com>2011-09-21 05:42:30 -0400
committerAlex Elder <aelder@sgi.com>2011-10-11 22:15:07 -0400
commitc029a50d51b8a9520105ec903639de03389915d0 (patch)
treef0d3993b6429eea9f4843a3113eb9258613f30ef
parentd952e2f81244d6502aff126df5011fab10f92187 (diff)
xfs: fix possible overflow in xfs_ioc_trim()
In xfs_ioc_trim it is possible that computing the last allocation group to discard might overflow for big start & len values, because the result might be bigger then xfs_agnumber_t which is 32 bit long. Fix this by not allowing the start and end block of the range to be beyond the end of the file system. Note that if the start is beyond the end of the file system we have to return -EINVAL, but in the "end" case we have to truncate it to the fs size. Also introduce "end" variable, rather than using start+len which which might be more confusing to get right as this bug shows. Signed-off-by: Lukas Czerner <lczerner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Alex Elder <aelder@sgi.com>
-rw-r--r--fs/xfs/xfs_discard.c20
1 files changed, 10 insertions, 10 deletions
diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c
index 244e797dae32..8a24f0c6c860 100644
--- a/fs/xfs/xfs_discard.c
+++ b/fs/xfs/xfs_discard.c
@@ -38,7 +38,7 @@ xfs_trim_extents(
38 struct xfs_mount *mp, 38 struct xfs_mount *mp,
39 xfs_agnumber_t agno, 39 xfs_agnumber_t agno,
40 xfs_fsblock_t start, 40 xfs_fsblock_t start,
41 xfs_fsblock_t len, 41 xfs_fsblock_t end,
42 xfs_fsblock_t minlen, 42 xfs_fsblock_t minlen,
43 __uint64_t *blocks_trimmed) 43 __uint64_t *blocks_trimmed)
44{ 44{
@@ -100,7 +100,7 @@ xfs_trim_extents(
100 * down partially overlapping ranges for now. 100 * down partially overlapping ranges for now.
101 */ 101 */
102 if (XFS_AGB_TO_FSB(mp, agno, fbno) + flen < start || 102 if (XFS_AGB_TO_FSB(mp, agno, fbno) + flen < start ||
103 XFS_AGB_TO_FSB(mp, agno, fbno) >= start + len) { 103 XFS_AGB_TO_FSB(mp, agno, fbno) > end) {
104 trace_xfs_discard_exclude(mp, agno, fbno, flen); 104 trace_xfs_discard_exclude(mp, agno, fbno, flen);
105 goto next_extent; 105 goto next_extent;
106 } 106 }
@@ -145,7 +145,7 @@ xfs_ioc_trim(
145 struct request_queue *q = mp->m_ddev_targp->bt_bdev->bd_disk->queue; 145 struct request_queue *q = mp->m_ddev_targp->bt_bdev->bd_disk->queue;
146 unsigned int granularity = q->limits.discard_granularity; 146 unsigned int granularity = q->limits.discard_granularity;
147 struct fstrim_range range; 147 struct fstrim_range range;
148 xfs_fsblock_t start, len, minlen; 148 xfs_fsblock_t start, end, minlen;
149 xfs_agnumber_t start_agno, end_agno, agno; 149 xfs_agnumber_t start_agno, end_agno, agno;
150 __uint64_t blocks_trimmed = 0; 150 __uint64_t blocks_trimmed = 0;
151 int error, last_error = 0; 151 int error, last_error = 0;
@@ -165,19 +165,19 @@ xfs_ioc_trim(
165 * matter as trimming blocks is an advisory interface. 165 * matter as trimming blocks is an advisory interface.
166 */ 166 */
167 start = XFS_B_TO_FSBT(mp, range.start); 167 start = XFS_B_TO_FSBT(mp, range.start);
168 len = XFS_B_TO_FSBT(mp, range.len); 168 end = start + XFS_B_TO_FSBT(mp, range.len) - 1;
169 minlen = XFS_B_TO_FSB(mp, max_t(u64, granularity, range.minlen)); 169 minlen = XFS_B_TO_FSB(mp, max_t(u64, granularity, range.minlen));
170 170
171 start_agno = XFS_FSB_TO_AGNO(mp, start); 171 if (start >= mp->m_sb.sb_dblocks)
172 if (start_agno >= mp->m_sb.sb_agcount)
173 return -XFS_ERROR(EINVAL); 172 return -XFS_ERROR(EINVAL);
173 if (end > mp->m_sb.sb_dblocks - 1)
174 end = mp->m_sb.sb_dblocks - 1;
174 175
175 end_agno = XFS_FSB_TO_AGNO(mp, start + len); 176 start_agno = XFS_FSB_TO_AGNO(mp, start);
176 if (end_agno >= mp->m_sb.sb_agcount) 177 end_agno = XFS_FSB_TO_AGNO(mp, end);
177 end_agno = mp->m_sb.sb_agcount - 1;
178 178
179 for (agno = start_agno; agno <= end_agno; agno++) { 179 for (agno = start_agno; agno <= end_agno; agno++) {
180 error = -xfs_trim_extents(mp, agno, start, len, minlen, 180 error = -xfs_trim_extents(mp, agno, start, end, minlen,
181 &blocks_trimmed); 181 &blocks_trimmed);
182 if (error) 182 if (error)
183 last_error = error; 183 last_error = error;