aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAlexey Dobriyan <adobriyan@sw.ru>2007-07-16 02:40:39 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:45 -0400
commitcb510b8172602a66467f3551b4be1911f5a7c8c2 (patch)
tree4905ee309e3147bb64f8a8a4446937424540fd5f /fs
parent18d96779d92902d2113b6f39bd2d42e805fa05e7 (diff)
seq_file: more atomicity in traverse()
Original problem: in some circumstances seq_file interface can present infinite proc file to the following script when normally said proc file is finite: while read line; do [do something with $line] done </proc/$FILE bash, to implement such loop does essentially read(0, buf, 128); [find \n] lseek(0, -difference, SEEK_CUR); Consider, proc file prints list of objects each of them consists of many lines, each line is shorter than 128 bytes. Two objects in list, with ->index'es being 0 and 1. Current one is 1, as bash prints second object line by line. Imagine first object being removed right before lseek(). traverse() will be called, because there is negative offset. traverse() will reset ->index to 0 (!). traverse() will call ->next() and get NULL in any usual iterate-over-list code using list_for_each_entry_continue() and such. There is one object in list now after all... traverse() will return 0, lseek() will update file position and pretend everything is OK. So, what we have now: ->f_pos points to place where second object will be printed, but ->index is 0. seq_read() instead of returning EOF, will start printing first line of first object every time it's called, until enough objects are added to ->f_pos return in bounds. Fix is to update ->index only after we're sure we saw enough objects down the road. Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/seq_file.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/seq_file.c b/fs/seq_file.c
index e8e51db4989d..bbb19be260ce 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -177,21 +177,23 @@ EXPORT_SYMBOL(seq_read);
177 177
178static int traverse(struct seq_file *m, loff_t offset) 178static int traverse(struct seq_file *m, loff_t offset)
179{ 179{
180 loff_t pos = 0; 180 loff_t pos = 0, index;
181 int error = 0; 181 int error = 0;
182 void *p; 182 void *p;
183 183
184 m->version = 0; 184 m->version = 0;
185 m->index = 0; 185 index = 0;
186 m->count = m->from = 0; 186 m->count = m->from = 0;
187 if (!offset) 187 if (!offset) {
188 m->index = index;
188 return 0; 189 return 0;
190 }
189 if (!m->buf) { 191 if (!m->buf) {
190 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); 192 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
191 if (!m->buf) 193 if (!m->buf)
192 return -ENOMEM; 194 return -ENOMEM;
193 } 195 }
194 p = m->op->start(m, &m->index); 196 p = m->op->start(m, &index);
195 while (p) { 197 while (p) {
196 error = PTR_ERR(p); 198 error = PTR_ERR(p);
197 if (IS_ERR(p)) 199 if (IS_ERR(p))
@@ -204,15 +206,17 @@ static int traverse(struct seq_file *m, loff_t offset)
204 if (pos + m->count > offset) { 206 if (pos + m->count > offset) {
205 m->from = offset - pos; 207 m->from = offset - pos;
206 m->count -= m->from; 208 m->count -= m->from;
209 m->index = index;
207 break; 210 break;
208 } 211 }
209 pos += m->count; 212 pos += m->count;
210 m->count = 0; 213 m->count = 0;
211 if (pos == offset) { 214 if (pos == offset) {
212 m->index++; 215 index++;
216 m->index = index;
213 break; 217 break;
214 } 218 }
215 p = m->op->next(m, p, &m->index); 219 p = m->op->next(m, p, &index);
216 } 220 }
217 m->op->stop(m, p); 221 m->op->stop(m, p);
218 return error; 222 return error;